如何在 React Native 中更新 FlatList 中的单个项目? [英] How to update a single item in FlatList in React Native?

查看:61
本文介绍了如何在 React Native 中更新 FlatList 中的单个项目?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:我已经在那里发布了一个答案,我个人认为这是迄今为止最好的解决方案.尽管这不是评分最高的答案,但根据我得到的结果,它非常有效.

Attention: I have posted an answer down there, personally I think it's the best solution so far. Even though it's not the highest rated answer, but based on the result I'm getting, it is very efficient.

----------------------------------------------原始问题-------------------------------------------------------

---------------------------------------------Original Question-------------------------------------------------------

假设我正在写一个 Twitter 克隆,但要简单得多.我将每个项目放入 FlatList 并渲染它们.

Suppose I am writing a Twitter clone, 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 red, I press it again, it turns gray.

这是我目前所拥有的:我将所有加载的帖子存储在 this.state 中,每个帖子都有一个名为liked"的属性,它是布尔值,表示该用户是否喜欢这个无论是否发布,当用户按下赞"时,我都会转到 state.posts 并更新该帖子的 liked 属性,然后使用 this.setState 像这样更新帖子:

This is what I have so far: I store all the loaded posts in this.state, each post has a property called "liked", which is boolean, indicating whether this user has liked this post or not, when user presses "like", I go to state.posts and update the liked property of that post, and then use this.setState to update posts like so:

// 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 });
}

这种方法有效,但是速度太慢.当我按下喜欢"按钮时,它的颜色会翻转,但通常需要大约 1 秒钟才能改变颜色.我想要的是当我按下它时颜色几乎同时翻转.

This approach works, however, it is too slow. The color of the "like" button flips as I press it, but it usually takes about 1 second before the color changes. What I want is that the color would flip almost at the same time when I press it.

我知道为什么会发生这种情况,我可能不应该使用 this.setState,因为当我这样做时,posts 状态改变了,所有的帖子都会重新-rendered,但我还能尝试什么其他方法?

I do know why this would happen, I should probably not use this.setState, because when I do that, the posts state changed, and all posts get re-rendered, but what other approach can I try?

推荐答案

不要误会我的意思,@ShubhnikSingh 的回答确实有帮助,但我撤回了它,因为我很久以前找到了更好的解决方案,终于我记得把它贴在这里.

Don't get me wrong, @ShubhnikSingh's answer did help, but I retracted it because I found a better solution to this question, long time ago, and finally I remembered to post it here.

假设我的帖子项目包含以下属性:

Suppose my post item contains these properties:

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

Where liked 代表查看这个帖子的用户是否喜欢这个帖子,这将决定喜欢"按钮的颜色(默认情况下,它是灰色的,但如果 liked == true)

Where liked represents whether the user viewing this post has liked this post, which will determine the color of the "like" button (by default, it's gray, but red if liked == true)

以下是重新创建我的解决方案的步骤:使发布"成为 Component 并将其呈现在 FlatList 中.你可以使用 React 的 PureComponent 如果你没有任何你传递给你的 Post 的 props,比如一个数组或对象,它们可能看起来不是浅相等.如果您不知道这意味着什么,只需使用常规的 Component 并覆盖 shouldComponentUpdate,如下所示.

Here are the steps to recreate my solution: make "Post" a Component and render it in a FlatList. You can use React's PureComponent if you don't have any props that you pass to your Post such as an array or object that can be deceptively not shallow equal. If you don't know what that means, just use a regular Component and override shouldComponentUpdate as we do below.

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 组件,负责处理加载帖子和处理类似交互的逻辑:


Then, create a PostList component that will be in charge of handling the logic for loading posts and handling like interactions:

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"中的每一项

1) Write a custom component (Post) for rendering each item in "FlatList"

2) 覆盖自定义组件的shouldComponentUpdate"(Post)函数,告诉组件何时更新

2) Override the "shouldComponentUpdate" of the custom component (Post) function to tell the component when to update

在父组件(PostList)中处理点赞状态"并将数据向下传递给每个子组件

Handle the "state of likes" in a parent component (PostList) and pass data down to each child

这篇关于如何在 React Native 中更新 FlatList 中的单个项目?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆