I have a two part question. I'm currently using react-native-snap-carousel found here: https://github.com/meliorence/react-native-snap-carousel .
My code looks like this:
carouselItems: [
{
image: require('../assets/images/placeholder-slide.png'),
},
{
image: require('../assets/images/slide2.png'),
},
{
//I want to pass <View> here with content and a button
},
]
_renderItem({ item, index }) {
<View>
<Image source={item.image} />
</View>
}
<SafeAreaView style={styles.body}>
<Carousel
layout={"default"}
ref={ref => this.carousel = ref}
data={this.state.carouselItems}
sliderWidth={phoneWidth}
itemWidth={phoneWidth}
renderItem={this._renderItem}
inactiveSlideOpacity={1}
inactiveSlideScale={1}
slideStyle={{ width: phoneWidth }}
onSnapToItem={index => this.setState({ activeIndex: index })} />
//This is the block I want to pass in the 3rd slide of the carousel
<View>
<Text>Some Text Here</Text>
<button>Button here</button>
</View>
</SafeAreaView>
body: {
flex: 1,
position: 'relative',
},
So the first two items in the carousel are a full-width image, the last slide will contain content and 2 buttons. How do I pass the items in the <View> into the carouselItems?
My second question is how do I make the carousel full screen. Here is a screenshot of my simulator: https://ibb.co/ZNZKRqm
As you can see there is grey area above and below the carousel. I don't believe this is something to do with the carousel, I think maybe by default this is full width? But I have seen applications target this gray space as well.
Part
carouselItems: [
{
image: require('../assets/images/placeholder-slide.png'),
},
{
image: require('../assets/images/slide2.png'),
},
{
content:
<View>
<Text>Some Text Here</Text>
<button>Button here</button>
</View>
},
]
_renderItem({ item, index }) {
//return item.content if exists
return item?.content || <View>
<Image source={item.image} />
</View>
}
<SafeAreaView style={styles.body}>
<Carousel
layout={"default"}
ref={ref => this.carousel = ref}
data={this.state.carouselItems}
sliderWidth={phoneWidth}
itemWidth={phoneWidth}
renderItem={this._renderItem}
inactiveSlideOpacity={1}
inactiveSlideScale={1}
slideStyle={{ width: phoneWidth }}
onSnapToItem={index => this.setState({ activeIndex: index })} />
</SafeAreaView>
Part
The grey space is because of theSafeAreaView. This will only be on the devices with a notch. You can set custom backgroundColor for it with a style prop.
Related
I have a multiple Pressable component. How can I make it that when I clicked on the Motorcycle pressable, the check icon would be shown beside it and if on the Tricycle Pressable, the check icon would be shown beside it and the one on the Motorcycle icon will be gone.
I have tried creating a state but it simultaneously set all icons in place. Here is the code:
const [checkIcon, setCheckIcon] = useState(false)
<Pressable
style={({ pressed }) => [{ opacity: pressed ? 0.4 : 1 }, styles.modalField]}
onPress={() => setCheckIcon(true)}
>
<Image source={require("../assets/motorcycle.png")} style={styles.modalFieldImage} />
<View style={styles.modalFieldVehicleNameContainer}>
<Text style={styles.modalFieldText}>Motorcycle</Text>
<Text style={styles.modalFieldTextDescription}>Cheapest option perfect for small-sized items</Text>
<Text style={styles.modalFieldTextDescription}>Up to 20 kg</Text>
</View>
{
checkIcon === true ? <Icon name="check" type="font-awesome-5" size={hp("3%")} color="#322C6A" style={styles.modalFieldIcon} /> : null
}
</Pressable>
<Pressable
style={({ pressed }) => [{ opacity: pressed ? 0.4 : 1 }, styles.modalField]}
onPress={() => setCheckIcon(true)}
>
<Image source={require("../assets/tricycle.png")} style={styles.modalFieldImage} />
<View style={styles.modalFieldVehicleNameContainer}>
<Text style={styles.modalFieldText}>Tricycle</Text>
<Text style={styles.modalFieldTextDescription}>Perfect for multiple medium-sized items</Text>
<Text style={styles.modalFieldTextDescription}>Up to 70 kg</Text>
</View>
{
checkIcon === true ? <Icon name="check" type="font-awesome-5" size={hp("3%")} color="#322C6A" style={styles.modalFieldIcon} /> : null
}
</Pressable>
<Pressable
style={({ pressed }) => [{ opacity: pressed ? 0.4 : 1 }, styles.modalField]}
onPress={() => setCheckIcon(true)}
>
<Image source={require("../assets/sedan.png")} style={styles.modalFieldImage} />
<View style={styles.modalFieldVehicleNameContainer}>
<Text style={styles.modalFieldText}>Sedan Car</Text>
<Text style={styles.modalFieldTextDescription}>Good for cakes and multiple small to medium-sized items</Text>
<Text style={styles.modalFieldTextDescription}>Up to 200 kg</Text>
</View>
{
checkIcon === true ? <Icon name="check" type="font-awesome-5" size={hp("3%")} color="#322C6A" style={styles.modalFieldIcon} /> : null
}
</Pressable>
Here is an simple example, using a index state to log down which item is selected by user.
Since your list can be generated dynamically, so you may use an array to store all the parameters for rendering and use map() to render one by one.
const [optionArray, setOptionArray] = useState([
{
title: "Motorcycle",
desc1: "Cheapest option perfect for small-sized items",
desc2: "Up to 20 kg",
img: "../assets/motorcycle.png",
},
{
title: "Tricycle",
desc1: "Perfect for multiple medium-sized items",
desc2: "Up to 70 kg",
img: "../assets/tricycle.png"
},
{
title: "Sedan Car",
desc1: "Good for cakes and multiple small to medium-sized items",
desc2: "Up to 200 kg",
img: "../assets/sedan.png",
}
]);
const [selectedIndex, setSelectedIndex] = useState(-1); //Nothing is selected with initial render
const ListItem = ({option, index}) =>{
return(
<Pressable
style={({ pressed }) => [{ opacity: pressed ? 0.4 : 1 }, styles.modalField]}
onPress={() => setSelectedIndex(index)}
>
<Image source={option.img} style={styles.modalFieldImage} />
<View style={styles.modalFieldVehicleNameContainer}>
<Text style={styles.modalFieldText}>{option.title}</Text>
<Text style={styles.modalFieldTextDescription}>{option.desc1}</Text>
<Text style={styles.modalFieldTextDescription}>{option.desc2}</Text>
</View>
{
index === selectedIndex && <Icon name="check" type="font-awesome-5" size={hp("3%")} color="#322C6A" style={styles.modalFieldIcon} />
}
</Pressable>
)
}
return(
<View>
{
optionArray.map((option, index) =>
<ListItem option={option} index={index} />
)
}
</View>
)
Also answer by Hardik prajapati is another solution too. That method will not affect the result when the data list is sorted in runtime. But large object modification will trigger re-render for components which may lead to performance issue in some case.
Therefore, you can select different approach for your case.
create Vehicle item array like
vehicle:[ image:'put your imageUrl', name:'', description:'', clickStatus:true ]
then use map method to display all array item and
when you click on any item update your array with clickStatus key
pressable item true and other two item false and display Check Icon based on true or false
I have a scrollview which has a flatlist nested inside of it, so now I want to scroll to a certain position of the scrollview on button press, how can I achieve this?
Here is my code:
<ScrollView
ref={verticalRef}
>
{
data.map( item => (
<View>
<Text style={styles.title}>{item.title}</Text>
<View >
<FlatList
data={item.data}
numColumns={2}
renderItem={thitem}
/>
</View>
</View>
))
}
</ScrollView>
Here is the handler for the scroll:
const scrollToActiveIndex = (index) => {
setActiveIndex(index)
verticalRef.current?.scrollTo({
x: width * index,
animated: true
})
}
I have also attached a screenshot of my app. Thanksenter image description here
Please help my deal with unnecessary re-renders when having two flatlists on the same screen
My screen requires two flatlists-
For HOSTS
For QUEUE
When the component mounts, I get data from the api call like this-
{
"hosts": [{"id":"1", "name":"kyouma"},...],
"queue": [{"id":"99", "name":"eren"},...]
}
Now what I do is I store my hosts and queue separately in my redux store like this-
this.props.dispatch({
type: GET_ROOM_HOSTS,
payload: info['hosts']
})
this.props.dispatch({
type: GET_ROOM_QUEUE,
payload: info['queue']
})
where info is the object received from the api call as shown above. Now I mapStateToProps these two from the redux store to the default screen component such that-
this.props.roomQueue is for queue and
this.props.roomHosts is for hosts
My FlatList's are like this-
<FlatList
data={this.props.roomQueue}
horizontal={true}
keyExtractor = {item => item.id}
renderItem({item} => {
return(
<customComponent (with suitable props) ..../>
)
})
/>
<FlatList
data={this.props.roomHosts}
numColumns={3}
keyExtractor = {item => item.id}
renderItem({item} => {
return(
<customComponent (with suitable props) ..../>
)
})
/>
PLEASE NOTE that both the FlatList's are present in the same Component (React.Component) of the screen and are displayed at different parts of the screen(queue at bottom of the screen and hosts at the top of the screen). Also queue and hosts are independent of each other. Screen looks like this
My problem is that even if there is a change in this.props.roomQueue, the FlatList having its data={this.props.roomHosts} get's re-rendered.
How do i prevent this re-render to ensure that only if the FlatList's corresponding data changes, then only will it re-render, otherwise it won't. Do I have to change the way I store queue and hosts? or is there something else?
You can do this with using only one flatlist. Merge your both array's into one and show results from one list.. you can spare them in ui with a type.
This is a genuine procedure of what developers do, cz rendering 2 list in same page and same direction is accually no mean. Your query is valid.
You can add List ListFooterComponent and it will automatically do this for you
<FlatList
contentContainerStyle={{
width: WINDOW_WIDTH,
paddingVertical:WINDOW_WIDTH*0.2,
marginLeft:10
}}
ListFooterComponent={()=> returnYourViewDesignHere()}
columnWrapperStyle={{ flex: 1, justifyContent: "space-around" }}
keyExtractor={(item) => item.id}
onEndReached={() => getPaginationData()}
onEndReachedThreshold={0.0001}
numColumns={3}
showsVerticalScrollIndicator={false}
data={allShows}
renderItem={({ item, index }) => {
return (
<TouchableWithoutFeedback
key={item.index + Math.floor(Math.random() * 1000)}
onPress={() =>
props.navigation.navigate(
item.type == "movie" ? "MovieDetailScreen" : "SeasonDetail",
{
data: item,
object: {
id: item.id,
},
}
)
}
>
<View style={styles.boxContainer}>
<View style={styles.imageBackground}>
<Text style={styles.backgroundText}>KEIN</Text>
<Text
style={[styles.backgroundText, { color: COLOR.primary }]}
>
POSTER
</Text>
</View>
<Image
source={{
uri: item.coverUrl ? item.coverUrl : item.coverPath,
}}
style={styles.imageBox}
resizeMode={"stretch"}
/>
<Text
numberOfLines={2}
ellipsizeMode={"tail"}
style={styles.text}
>
{item.showTitle ? item.showTitle : item.title}
</Text>
{userWatchedList.some((uwl) => uwl.id == item.id) ? (
<TouchableWithoutFeedback
onPress={() =>
isloggedIn
? removeFromUserWatchList(item)
: handleModalVisibility()
}
>
<Image
source={WATCHLIST_CHECKED}
style={{
width: 25,
height: 25,
position: "absolute",
right: 5,
top: 5,
}}
/>
</TouchableWithoutFeedback>
) : (
<TouchableWithoutFeedback
onPress={() =>
isloggedIn
? addToUserWatchList(item)
: handleModalVisibility()
}
>
<Image
source={CIRCLE_UNCHECKED}
style={{
width: 25,
height: 25,
position: "absolute",
right: 5,
top: 5,
}}
/>
</TouchableWithoutFeedback>
)}
</View>
</TouchableWithoutFeedback>
);
}}
/>
I have a state this.state.mantras which gets updated everytime I post a new mantra.
I have a component with a FlatList to render each of the mantras. However, the FlatList is not rendering anything. I am able to console.log the state which logs the updated state of the mantras array every time.
I have a feeling that it's caused by something to do with react-native-tab-view within which I am trying to render the flatlist.
SecondRoute = () => (
<FlatList
keyExtractor={(item, index) => index.toString()}
numColumns={1}
data={this.state.mantras}
renderItem={this._renderItem}
/>
)
render() {
console.log(this.state) <-- success
return (
<TabView
renderTabBar={props =>
<TabBar
bounces
{...props}
style={styles.tabStyle}
/>
}
navigationState={this.state}
renderScene={SceneMap({
second: this.SecondRoute, <--- fails to render, no errors
})}
onIndexChange={index => this.setState({ index })}
initialLayout={{ width: Dimensions.get('window').width }}
/>
)
}
_renderItem = ({item, index}) => (
<View>
<View style={styles.mantraCard} key={item.id}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.description}>{item.description}</Text>
</View>
</View>
);
last month, I met the problem when react-native-scrollable-tab-view. the reason is the flatList no height in tab view on android platform. but on ios, it works well. so we have two solutions.
give the tabView a height
<ScrollableTabView
style={{ height: 300 }
initialPage={0}
}
>
// you list view
</ScrollableTabView>
2, I modify the scrollTabView, made it same with ios, which use animated.ScrollView
renderScrollableContent() {
{
const scenes = this._composeScenes();
return <Animated.ScrollView
horizontal
pagingEnabled
automaticallyAdjustContentInsets={false}
contentOffset={{ x: this.props.initialPage * this.state.containerWidth,
}}
ref={(scrollView) => { this.scrollView = scrollView; }}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { x: this.state.scrollXIOS, }, }, },
],
{ useNativeDriver: true, listener: this._onScroll, }
)}
onMomentumScrollBegin={this._onMomentumScrollBeginAndEnd}
onMomentumScrollEnd={this._onMomentumScrollBeginAndEnd}
scrollEventThrottle={16}
scrollsToTop={false}
showsHorizontalScrollIndicator={false}
scrollEnabled={!this.props.locked}
directionalLockEnabled
alwaysBounceVertical={false}
keyboardDismissMode="on-drag"
{...this.props.contentProps}
>
{scenes}
</Animated.ScrollView>;
}
friend I Will integrated checked - unchecked in listView. So that When user click on checked then store the data in Array and unchecked then i will remove the data. Its working fine, But the UI Will not updated after checked - unchecked.
<List containerStyle={{marginTop : 0 , borderBottomWidth : 0 , borderBottomColor : 'black', borderTopWidth : 0}}>
<FlatList
data={this.state.list}
renderItem={({ item }) => (
<ListItem containerStyle={{height: 80, backgroundColor : 'transparent', borderBottomWidth : 0, borderTopWidth : 0}}
title={
<View style={styles.titleView}>
<Text style={styles.ratingText}>{item.iWorkerID.vFirstName}</Text>
</View>
}
rightIcon={
<TouchableOpacity onPress = {() => this.selectedWorker(item)} style={{width: 30, height: 30 , marginTop : 10, marginRight : 30}}>
<Image style = {{width: 30, height: 30}} source={this.state.selectedList.includes(item) ? require("./Images/uncheckd.png") : require("./Images/checked.png")}/>
{/* {this.state.selectedList.includes(item) && <Image style = {{width: 30, height: 30}} source={require("./Images/uncheckd.png")}/>}
{!this.state.selectedList.includes(item) && <Image style = {{width: 30, height: 30}} source={require("./Images/checked.png")}/>} */}
</TouchableOpacity>
}
avatar={<Avatar
rounded
medium
containerStyle={{marginLeft: 30}}
source={{uri: Globle.IMAGE_URL+item.vProfileImage}}
activeOpacity={0.7}
/>}
/>
)}
/>
</List>
And on the check/uncheck button, I will add/remove object from array,
selectedWorker = (data) =>{
console.log('data is ', data);
if (!this.state.selectedList.includes(data)) {
// this.setState({ selectedList : [...this.state.selectedList , data]})
this.state.selectedList.push(data);
} else {
var index = this.state.selectedList.indexOf(data);
if (index > -1) {
this.state.selectedList.splice(index, 1);
}
}
this.setState({list : this.state.list})
console.log('selected list' , this.state.selectedList);
}
Main Issue : Want to update image checked/unchecked according to selectedList array, How can i Update item in listView.
What to do inside selectedWorker method.
GIF :
you are using Flatelist inside List, Both are a component to listing items. you can use List or Flatelist, not both.
I hope it will help you..
I try to make Demo similar to that you want.
constructor(props) {
super(props)
this.state = {
list: [
{
id: 1,
name: "Harpal Singh Jadeja",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 2,
name: "Kirit Mode",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 3,
name: "Rajiv Patil",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 4,
name: "Chetan Doctor",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
}]
};
};
renderListItem = (index, item) => {
return (
<View style={styles.notification_listContainer}>
<Image source={{uri: item.avtar, cache: 'force-cache'}}
style={circleStyle}/>
<View style={{flex: 1, paddingLeft: 10, justifyContent: 'center'}}>
<Label roboto_medium
align='left'
color={Color.TEXT_PRIMARY}
numberOfLines={1}>
{item.name}
</Label>
<Label roboto_medium
small
align='left'
color={Color.TEXT_SECONDARY}
mt={8}>
Programmer
</Label>
</View>
<View style={{justifyContent: 'center'}}>
<TouchableHighlight
style={{
backgroundColor: item.isSelected ? Color.BLACK : Color.TEXT_SECONDARY,
alignItems: 'center',
justifyContent: 'center',
height: 40,
width: 40,
borderRadius: 20
}}
onPress={this.onSelectWorker.bind(this, index, item)} underlayColor={Color.BLACK}>
<Icon name='done'
size={20}
color={Color.WHITE}/>
</TouchableHighlight>
</View>
</View>
);
};
onSelectWorker = (index, item) => {
console.log("Selected index : ", index);
let tempList = this.state.list;
tempList[index].isSelected = tempList[index].isSelected ? false : true
this.setState({
list: tempList
});
};
render() {
return (
<View style={styles.notification_Container}>
<FlatList
data={this.state.list}
renderItem={
({index, item}) => this.renderListItem(index, item)
}
keyExtractor={item => item.id}
extraData={this.state}
/>
</View>
)
}
You need to add a key to your ListItem which is based on a unique id of the item, so that React can distinguish between the items rendered.
When you use index of an array as a key, React will optimize and not render properly. What happens in such a scenario can be explained with an example.
Suppose React renders an array of 10 items and renders 10 components. Suppose the 5th item is then removed. On the next render React will receive an array of 9 items and so React will render 9 components. This will show up as the 10th component getting removed, instead of the 5th, because React has no way of differentiating between the items based on index.
Therefore always use a unique identifier as a key for components that are rendered from an array of items.