How to update single element in FlatList in React Native?

Attention : I posted the answer there, I personally think that this is the best solution at the moment. Despite the fact that this is not the highest response rating, but depending on the result that I get, it is very effective.

--------------------------------------------- Original question --- -------------------------------------------- ------ -

Suppose I am writing a Twitter clone, but much easier. I put each element in a FlatList and display them.

To like the message, I click the Like button on the message and the Like button turns red, I click it again, it turns gray.

This is what I have so far: I save all the downloaded posts to this.state, each post has a “Like” property, which is logical and indicates whether the user liked this post or not when the user clicks “Like”. I go into state.postsand update the property of likedthis message, and then use it this.setStateto update messages as follows:

// 1. FlatList
<FlatList
    ...
    data={this.state.posts}
    renderItem={this.renderPost}
    ...
/> 

// 2. renderPost
renderPost({ item, index }) {
    return (
        <View style={someStyle}>
            ... // display other properties of the post
            // Then display the "like" button
            <Icon
                name='favorite'
                size={25}
                color={item.liked ? 'red' : 'gray'}
                containerStyle={someStyle}
                iconStyle={someStyle}
                onPress={() => this.onLikePost({ item, index })}
            />
            ...
        </View>
    );
}

// 3. onLikePost
likePost({ item, index }) {
    let { posts } = this.state;
    let targetPost = posts[index];

    // Flip the 'liked' property of the targetPost
    targetPost.liked = !targetPost.liked;

    // Then update targetPost in 'posts'
    posts[index] = targetPost;

    // Then reset the 'state.posts' property
    this.setState({ posts });
}

This approach works, however it is too slow. The color of the Like button changes when clicked, but it usually takes about 1 second before the color changes. I want the color to change almost simultaneously with pressing.

, , , , this.setState, , , posts , , ?

+18
3

, @ShubhnikSingh , , , , .

, :

{
    postId: "-L84e-aHwBedm1FHhcqv",
    date: 1525566855,
    message: "My Post",
    uid: "52YgRFw4jWhYL5ulK11slBv7e583",
    liked: false,
    likeCount: 0,
    commentCount: 0
}

liked , , , " " ( , , liked == true)


: "Post" a Component FlatList. React PureComponent, , Post, , , . , , Component shouldComponentUpdate, .

class Post extends Component {                                                      
  // This determines whether a rendered post should get updated                     
  // Look at the states here, what could be changing as time goes by?               
  // Only 2 properties: "liked" and "likeCount", if the person seeing               
  // this post ever presses the "like" button                                       
  // This assumes that, unlike Twitter, updates do not come from other              
  // instances of the application in real time.                                     
  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() {                                                                        
    return (                                                                        
      <View>                                                                        
        {/* ...render other properties */}                                          
        <TouchableOpacity                                                           
          onPress={() => this.props.onPressLike(this.props.postId)}                 
        >                                                                           
          <Icon name="heart" color={this.props.liked ? 'gray' : 'red'} />           
        </TouchableOpacity>                                                         
      </View>                                                                       
    )                                                                               
  }                                                                                 
}                                                                                   

PostList, :

class PostList extends Component {                                                        

/**                                                                                       
 * As you can see, we are not storing "posts" as an array. Instead,                       
 * we make it a JSON object. This allows us to access a post more concisely               
 * than if we stores posts as an array. For example:                                      
 *                                                                                        
 * this.state.posts as an array                                                           
 * findPost(postId) {                                                                     
 *   return this.state.posts.find(post => post.id === postId)                             
 * }                                                                                      
 * findPost(postId) {                                                                     
 *   return this.state.posts[postId]                                                      
 * }                                                                                      
 * a specific post by its "postId", you won't have to iterate                             
 * through the whole array, you can just call "posts[postId]"                             
 * to access it immediately:                                                              
 * "posts": {                                                                             
 *     "<post_id_1>": { "message": "", "uid": "", ... },                                  
 *     "<post_id_2>": { "message": "", "uid": "", ... },                                  
 *     "<post_id_3>": { "message": "", "uid": "", ... }                                   
 * }                                                                                      
 * FlatList wants an array for its data property rather than an object,                   
 * so we need to pass data={Object.values(this.state.posts)} rather than                  
 * just data={this.state.posts} as one might expect.                                      
*/                                                                                        

  state = {                                                                                 
    posts: {}                                                                               
    // Other states                                                                         
  }                                                                                         

  renderItem = ({ item }) => {
    const { date, message, uid, postId, other, props, here } = item
    return (
      <Post
        date={date}
        message={message}
        uid={uid}
        onPressLike={this.handleLikePost}
      />
    )
  }

  handleLikePost = postId => {
    let post = this.state.posts[postId]
    const { liked, likeCount } = post

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

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

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

:

1) (Post) "FlatList"

2) shouldComponentUpdate (Post), ,

" " (PostList)

+7

extraData FlatList:

<FlatList
...
    extraData={this.state}
    data={this.state.posts}
    renderItem={this.renderPost}
    ...
/> 

state.posts state.posts FlatList .

FlatList # extradata:

( PureComponent). - renderItem, Header, Footer .. -, , .

+12

If you are testing on android, try disabling developer mode. Or do you click some kind of API and update the message on the server and update a similar button in the user interface corresponding to the server’s response? If so, tell me, I also ran into this, and I solved it. I also commented on the second last line in your code, which is not needed.

// 1. FlatList
<FlatList
    ...
    data={this.state.posts}
    renderItem={this.renderPost}
    ...
/> 

// 2. renderPost
renderPost({ item, index }) {
    return (
        <View style={someStyle}>
            ... // display other properties of the post
            // Then display the "like" button
            <Icon
                name='favorite'
                size={25}
                color={item.liked ? 'red' : 'gray'}
                containerStyle={someStyle}
                iconStyle={someStyle}
                onPress={() => this.onLikePost({ item, index })}
            />
            ...
        </View>
    );
}

// 3. onLikePost
likePost({ item, index }) {
    let { posts } = this.state;
    let targetPost = posts[index];

    // Flip the 'liked' property of the targetPost
    targetPost.liked = !targetPost.liked;

    // Then update targetPost in 'posts'
    // You probably don't need the following line.
    // posts[index] = targetPost;

    // Then reset the 'state.posts' property
    this.setState({ posts });
}
+8
source

Source: https://habr.com/ru/post/1688350/


All Articles