Search code examples
react-nativereduxredux-thunkreact-native-flatlistreact-props

How to update a single item in FlatList - React Native


In my case I am writing a Facebook clone like application, but much simpler. I put each item in FlatList and render them.

To "like" a post, I press the "like" button on the post and the "like" button turns yellow and the likes count increase by 1 ( Also I call addLike API Function after clicking ), I press it again, it turns gray and likes count decrease by one ( Also I Call removeLike API Funtion ).

This is what I have so far: I store all the loaded posts with redux - this.props, each post has a property called "liked", which is boolean, indicating whether this user has liked this post or not, when user presses "like", Now I doing when user like the post I call the addLike() action and fetch all the posts to feed again. I want to do this without fetching data again and again

FlatList Component

<FlatList
          style={styles.postList}
          data={this.props.postData}
          extraData={this.props}
          maxToRenderPerBatch={10}
          keyExtractor={item => {
            return item.id;
          }}
          ItemSeparatorComponent={() => {
            return <View style={styles.separator} />;
          }}
          renderItem={post => {
            const item = post.item;
            this.state.userisLiked = item.likedUsers.find(
              user => user.id == this.state.userDetails.id,
            );
            // console.log('Returning Is Liked ', isLiked);
            return (
              <View style={styles.card}>
                <View>
                  {item.postImage ? (
                    <TouchableOpacity
                      onPress={() =>
                        this.showSelectedImageFullView(item.postImage)
                      }>
                      <ImageBackground
                        style={styles.cardImage}
                        source={{
                          uri: Strings.AWSS3_POST_IMAGE + item.postImage,
                        }}>
                        <View style={styles.overlay} />
                      </ImageBackground>
                    </TouchableOpacity>
                  ) : (
                    <View></View>
                  )}
                </View>

                <View style={{flexDirection: 'row'}}>
                  <Image
                    source={
                      item.user.profilePicture
                        ? {
                            uri:
                              Strings.AWSS3_USER_PROFILE_AVATAR +
                              item.user.profilePicture,
                          }
                        : Images.IMAGE_PLACEHOLDER
                    }
                    style={styles.postUserImage}
                  />
                  <View style={{flexDirection: 'column'}}>
                    <Text style={styles.postUserName}>
                      {item.user.firstName} {item.user.lastName}
                    </Text>
                    <TimeAgo
                      style={styles.postedTime}
                      time={item.createdAt}
                      interval={20000}
                    />
                  </View>

                  <TouchableOpacity
                    style={styles.postMoreInfoIcon}
                    onPress={() => this.toggleModal(item)}>
                    <Image
                      source={Images.POST_MORE_INFO}
                      style={styles.postMoreInfoIcon}
                    />
                  </TouchableOpacity>
                </View>
                <TouchableOpacity onPress={() => this.homeMoreInfoScreen(item)}>
                  <View style={{flexDirection: 'column'}}>
                    <Text style={styles.postTitle}>{item.title}</Text>
                    <Text style={styles.postBody} numberOfLines={2}>
                      {item.description}
                    </Text>
                  </View>
                </TouchableOpacity>
                <View style={styles.cardFooter}>
                  <View style={{flexDirection: 'row'}}>
                    <TouchableOpacity
                      onPress={() => {
                        this.handleUserLikes(item);
                      }}>
                      {this.state.userisLiked ? (
                        <Image
                          source={Images.POST_LIKE_CHECKED}
                          style={{
                            width: 20,
                            height: 20,
                            resizeMode: 'contain',
                          }}
                        />
                      ) : (
                        <Image
                          source={Images.POST_LIKE_UNCHECKED}
                          style={{
                            width: 20,
                            height: 20,
                            resizeMode: 'contain',
                          }}
                        />
                      )}
                    </TouchableOpacity>
                    <Text
                      selectable={true}
                      onPress={() => this.toggleLikeModal(item)}
                      style={{
                        fontFamily: AppStyles.primaryFont,
                        fontSize: 15,
                        color: AppStyles.colorWhite,
                        marginLeft: 5,
                      }}>
                      {item.likesCount} Likes
                    </Text>
                  </View>

                  <View style={{flexDirection: 'row', marginLeft: 20}}>
                    <TouchableOpacity
                      onPress={() => this.homeMoreInfoScreen(item)}>
                      <Image
                        source={Images.POST_COMMENT}
                        style={{width: 20, height: 20, resizeMode: 'contain'}}
                      />
                    </TouchableOpacity>
                    <Text
                      selectable={true}
                      onPress={() => this.homeMoreInfoScreen(item)}
                      style={{
                        fontFamily: AppStyles.primaryFont,
                        fontSize: 15,
                        color: AppStyles.colorWhite,
                        marginLeft: 5,
                      }}>
                      {item.commentsCount} Comments
                    </Text>
                  </View>

                  <View
                    style={{
                      flexDirection: 'row',
                      marginLeft: 10,
                      position: 'absolute',
                      right: 100,
                      top: 20,
                    }}>
                  </View>

                  <View
                    style={{
                      flexDirection: 'row',
                      marginLeft: 10,
                      position: 'absolute',
                      right: 10,
                      top: 20,
                    }}>
                    <TouchableOpacity
                      onPress={() => this.homeMoreInfoScreen(item)}>
                      <Text
                        style={{
                          fontFamily: AppStyles.primaryFont,
                          fontSize: 15,
                          color: AppStyles.colorWhite,
                        }}>
                        Comment
                      </Text>
                    </TouchableOpacity>
                  </View>
                </View>
              </View>
            );
          }}
        />

Handle User Like Action

 //Add Likes to selected post click listner
  handleUserLikes = item => {
    //Check user already liked the post
    const isLiked = item.likedUsers.find(
      user => user.id == this.state.userDetails.id,
    );

    if (isLiked) {
      this.props.removeLikeFromPost(item.id, this.state.user_token);
      this.props.fetchPostData(this.state.user_token);
    } else {
      this.props.addLikeToPost(
        item.id,
        this.state.user_token,
        this.state.userDetails.id,
      );
      this.props.fetchPostData(this.state.user_token);
    }
  };

Solution

  • First of all thanks to @TRomesh for his answer.

    Based on that I found a solution for this issue easily

    My Button Action Call

     handleUserLikes = item => {
        this.props.addLikeToPost({
            item_id:item.id,
            user_token:this.state.user_token,
            user_id:this.state.userDetails.id,
          });
      };
    

    Redux Reducer Function -> This is the part I edited from above answer

    const userLike = (state, payload) => {
      console.log('TCL: userLike -> payload', payload.payload);
      console.log('TCL: userLike -> state.postData', state.postData);
    
      let item = state.postData.find(user => user.id == payload.payload.item_id);
      let local_data = state.postData;
      let isLiked = item.likedUsers.find(
        user => user.id === payload.payload.user_id,
      );
      if (isLiked) {
        local_data = local_data.map(data => {
          if (data.id == payload.payload.item_id) {
            data.likesCount = Number(data.likesCount - 1);
          }
          return data;
        });
      } else if (isLiked == undefined || isLiked == null) {
        local_data = local_data.map(data => {
          if (data.id == payload.payload.item_id) {
            data.likesCount = Number(data.likesCount + 1);
          }
          return data;
        });
      }
      return {...state, postData: local_data};
    };
    

    Cheers !