Andrew M Yasso
Andrew M Yasso

Reputation: 111

React Native FlatList not rendering when data updates (Redux)

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

Answers (3)

Joey
Joey

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

Nunchucks
Nunchucks

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

Andrew M Yasso
Andrew M Yasso

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

Related Questions