skip to Main Content

Building an app and needed to update the data state being rendered in a FlatList. Things were not working as I expected so I broke everything down to a simple example to better understand what was going on.

I feel like I am missing something. I determined from testing in this sample app that Flatlist is looking for an ordered key and has to be exactly ordered starting at 0 and so on…

My question is what am I missing? Is this actually a concrete requirement or do I something configured or coded incorrectly? I have not found any documentation stating this requirement of a very well ordered key.

Here is an example of the issue. If you click on the last item 44, you receive undefined is not an object as the lookup does not work to the state.

Full code example below;

Post

import React, { Component } from "react";
import { useState } from "react";
import { Button, UIManager, Platform, LayoutAnimation, Modal, Alert, Linking, Pressable, View, Text, StyleSheet, TouchableHighlight, Image, TextInput, FlatList, ActivityIndicator, Dimensions, SafeAreaView, TouchableOpacity, } from "react-native";
import { FlashList } from "@shopify/flash-list";
import { List, ListItem, Avatar } from "react-native-elements";
import { StatusBar } from "expo-status-bar";
import ParsedText from "react-native-parsed-text";
import AntDesign from "react-native-vector-icons/AntDesign";

class Post extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    const { liked, likeCount } = nextProps;
    const { liked: oldLiked, likeCount: oldLikeCount } = this.props;

    // If "liked" or "likeCount" is different, then update
    return liked !== oldLiked || likeCount !== oldLikeCount;
  }

  render() {
    console.log(this.props.liked);
    return (
      <View key={this.props.postid} style={styles.container}>
        <TouchableOpacity
          onPress={() => this.props.onPressLike(this.props.postid)}
        >
          <AntDesign
            name="like2"
            size={20}
            color={this.props.liked ? "royalblue" : "black"}
          />
        </TouchableOpacity>
        <Text> {this.props.postid}</Text>
      </View>
    );
  }
}

Home

export default class Home extends Component {
  constructor(props) {
    super(props);
    this.state = {
      posts: [
        {
          postid: "0",
          date: "115255668551",
          message: "My Post 1 was 4",
          uid: "52YgRFw4jWhYL5ulK11slBv7e583xa",
          commentCount: "1",
          likeCount: "1",
          liked: "true",
        },
        {
          postid: "1",
          date: "215255668551",
          message: "My Post 2",
          uid: "52YgRFw4jWhYL5ulK11slBv7e583xb",
          commentCount: "1",
          likeCount: "1",
          liked: "false",
        },
        {
          postid: "2",
          date: "315255668551",
          message: "My Post 3",
          uid: "52YgRFw4jWhYL5ulK11slBv7e583xc",
          commentCount: "1",
          likeCount: "1",
          liked: "true",
        },
        {
          postid: "3",
          date: "415255668551",
          message: "My Post 4",
          uid: "52YgRFw4jWhYL5ulK11slBv7e583xd",
          commentCount: "1",
          likeCount: "1",
          liked: "false",
        },
        {
          postid: "44",
          date: "515255668551",
          message: "My Post 44 does not work!!!!!!!!!!!!!!!!!",
          uid: "52YgRFw4jWhYL5ulK11slBv7e583xe",
          commentCount: "1",
          likeCount: "1",
          liked: "false",
        },
      ],
      postsThisIsWorking: [
        {
          postid: "0",
          date: "115255668551",
          message: "My Post 0",
          uid: "52YgRFw4jWhYL5ulK11slBv7e583xa",
          commentCount: "1",
          likeCount: "1",
          liked: "true",
        },
        {
          postid: "1",
          date: "215255668551",
          message: "My Post 1",
          uid: "52YgRFw4jWhYL5ulK11slBv7e583xb",
          commentCount: "1",
          likeCount: "1",
          liked: "false",
        },
        {
          postid: "2",
          date: "315255668551",
          message: "My Post 3",
          uid: "52YgRFw4jWhYL5ulK11slBv7e583xc",
          commentCount: "2",
          likeCount: "1",
          liked: "true",
        },
        {
          postid: "3",
          date: "415255668551",
          message: "My Post 3",
          uid: "52YgRFw4jWhYL5ulK11slBv7e583xd",
          commentCount: "1",
          likeCount: "1",
          liked: "false",
        },
        {
          postid: "4",
          date: "515255668551",
          message: "My Post 4",
          uid: "52YgRFw4jWhYL5ulK11slBv7e583xe",
          commentCount: "1",
          likeCount: "1",
          liked: "false",
        },
      ],
    };
  }

  renderItem = ({ item, index }) => {
    const { date, message, uid, postid, liked } = item;
    console.log("---index-----", index);
    return (
      <Post
        postid={postid}
        liked={liked}
        date={date}
        message={message}
        uid={uid}
        onPressLike={this.handleLikePost}
      />
    );
  };

  handleLikePost = (postid) => {
    console.log("----posts----", this.state.posts);
    console.log("---postid---", { postid });
    let post = this.state.posts[postid]; //error with undefined (evaluating post liked) ----
    console.log("----post----", post);
    const { liked, likeCount } = post;

    const newPost = {
      ...post,
      liked: !liked,
      likeCount: liked ? likeCount - 1 : likeCount + 1,
    };

    this.setState({
      posts: {
        ...this.state.posts,
        [postid]: newPost,
      },
    });
  };

  render() {
    console.log(this.state.posts);
    return (
      <View style={{ flex: 1 }}>
        <FlatList
          data={Object.values(this.state.posts)}
          renderItem={this.renderItem}
          keyExtractor={(item, postid) => postid}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 50,
    backgroundColor: "#ecf0f1",
    padding: 8,
  },
});

3

Answers


  1. Chosen as BEST ANSWER

    I was able to figure out how to properly do the lookup on the object, I needed to use the index which is assigned starting at 0 as the rows are rendered. Works perfectly now...

    handleLikePost = index => {
        let post = this.state.posts[index]   //lookup by index
        const { liked, likeCount } = post
        const newPost = {
            ...post,
            liked: !liked,
            likeCount: liked ? likeCount - 1 : likeCount + 1
        }
        this.setState({
            posts: {
                ...this.state.posts,
                [index]: newPost
            }
        })
    }
    

  2. This is nothing to do with FlatList, or with React for that matter. Your posts is defined as an array in the constructor, but you’re trying to access it as an object (this.state.posts[postid]). (Note that postid contains string values, not numbers, which would have allowed array access to work like that if the postids were always sequential integers starting with zero. Though that seems unlikely in real usage.)

    To access it like that, your data structure should be

    posts: {
      "0": {postid: "0", likeCount: 1, etc: "etc"},
      "1": {postid: "1", likeCount: 1, etc: "etc"}
    ...
    }
    

    (Later in your code you have a setState that also assumes posts is an object using the above format; it’s only the state data in your constructor that’s in an array.)

    Login or Signup to reply.
  3. The initial state of the posts is a array. When you’re updating the state in handleLikePost you’re setting the posts to a object.

    You could do something like this to work with the array. Map over all the posts and modify the post if the id is the same. This will return a new array which you can then set to the state.

    handleLikePost = (postidToUpdate) => {
      const updatedPosts = this.state.posts.map((post) => {
        if (post.postid !== postidToUpdate) return post;
    
        const updatedLikeCount = post.liked
          ? parseInt(post.likeCount) - 1
          : parseInt(post.likeCount) + 1;
    
        return {
          ...post,
          liked: !post.liked,
          likeCount: updatedLikeCount.toString(),
        };
      });
    
      this.setState({
        posts: updatedPosts,
      });
    };
    

    I updated the likeCount to parse the int for adding and subtracting. And when returning the new post convert it back to a string.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search