Reputation: 111
I have an issue where my FlatList is not rendering the item I'm asking it to. That data I'm using to render the list is successfully available from my Redux store, and I'm hydrating the store in the componentDidMount of the same component I am rendering my FlatList in. I notice the component hits the render method twice, however it still doesn't seem to have the data by the time it needs to supply it to the renderItem prop.
I put a debugger in my CardBuilder, and noticed that it is hit once per item the first time, however since this.props.picskit is undefined, it won't render. It never gets hit a second time, even though in the parent element the picskitz have been hydated in the store and are accessible. How can I ask my FlatList to attempt to render again with the original data source? I eventually want to add infinite scrolling to this, but for now I'm just trying to get the first 25 values (single page) to at least render. Any help greatly appreciately.
Here is my componentDidMount:
componentDidMount(){
const { dispatch, user, token, history } = this.props
const { pageNumber } = this.state
const LOGIN = 'LOGIN'
if ( token && user ){
dispatch({ type: LOGIN, user: {user, token} })
dispatch(getAllPicskitz(pageNumber, token, history))
} else {
dispatch(getAllPicskitz(pageNumber, user.token, history))
}
}
Here is my FlatList:
keyExtractor = ( picskit, index) => picskit.id.toString()
render(){
const { picskitz } = this.props
return(
<View style={styles.container}>
<View style={styles.headerContainer}>
<Image
source={ require('../media/logo-with-color.png')}
/>
</View>
<FlatList
data={picskitz}
renderItem={ ({picskit}) => <CardBuilder picskit={picskit} /> }
keyExtractor={this.keyExtractor}
/>
<LowerNavBar />
</View>
)
}
And finally, here is the "CardBuilder" component I am trying to render:
render(){
const {picskit} = this.props
if ( picskit !== undefined ){
return (
<View style={styles.container}>
<View style={styles.headerContainer}>
<View style={styles.titleContainer}>
<Text style={styles.titleText}> {picskit.title} </Text>
</View>
<View style={styles.detailsContainer}>
<View style={styles.viewsContainer}>
<Image
source={ require('../media/icon-glasses.png')}
/>
<Text> {picskit.views} </Text>
</View>
<Text style={styles.detailsText}> {picskit.owner.name} </Text>
</View>
</View>
<ImageBackground
source={{uri: JSON.parse(picskit.content)[0].aws_url}}
style={styles.mainImage}
>
<Image
source={ require('../media/frame-black.png')}
style={{width: '100%', height: '100%'}}
/>
</ImageBackground>
</View>
)
}
return null
}
}
Upvotes: 1
Views: 2316
Reputation: 419
you forgot to pass extraData prop to your FlatList
extraData={picskitz}
this is what tells FlatList to rerender if {picskitz} value changes
Upvotes: 3
Reputation: 1230
If a Flatlist's data prop changes, then it will re-render automatically. This means that your data is not being passed successfully from your redux store to your component. Wherever you grab that data from the store and pass it as props is where I would start double checking my code & debugging.
Upvotes: 1
Reputation: 111
I found a potential answer, however I'm not sure it is the most performant or correct way to accomplish it. First, I added some conditional logic to only render my FlatList if I had data in the first place. It looks like this:
{ picskitz ?
<FlatList
data={picskitz}
renderItem={ ({item}) => <CardBuilder item={item} /> }
keyExtractor={(x, i) => i.toString() }
onEndReached={this.loadMore}
/>
:
null
}
Furthermore, upon looking at the React Native FlatList docs a little deeper, it turns out there is a built in prop. to handle this conditional logic I wrote. This prop allows you to render another component or perform a function when the dataSource/list is empty. This is what my updated FlatList component looks like, with the conditional taken out. My f => f basically means: do nothing.
<FlatList
data={picskitz}
renderItem={ ({item}) => <CardBuilder item={item} /> }
listEmptyComponent={ f => f }
keyExtractor={(x, i) => i.toString() }
onEndReached={this.loadMore}
/>
The next thing I did, was adjust my loadMore function to operate asynchronously. It looks like this:
loadMore = () => {
const { dispatch, user, history } = this.props
this.setState(
state => ({ pageNumber: state.pageNumber + 1 }), async () =>
await dispatch(getMorePicskitz(this.state.pageNumber, user.token, history))
)
}
You'll notice the dispatch is awaiting, making sure that the data is in the store correctly before render() is called again, meaning that the FlatList will now have the full list of 50 (from the previous 25 loaded) before rendering the CardBuilder component.
I am very open to suggestions if anyone has them!
Upvotes: 0