防止 FlatList 在状态改变时重新渲染整个列表

时间:2021-05-17 21:37:16

标签: reactjs react-native redux react-redux

我在 redux reducer 中有一个 <div class="box"> <div class="row header"> </div> <div class="row content"> </div> <div class="row footer"> </div> </div> 数组,我通过 postsonEndReached 属性更新。问题是,每次状态发生变化时,都会重新呈现整个列表,而不是受影响的帖子项。

FlatList

如您所见,我在 // Posts Feed Screen const posts = useSelector(state => state.posts) const renderItem = useCallback( ({ item }) => ( <Post post={item} /> ), [] ) const keyExtractor = useCallback( (item, index) => `${item.id}${index}`, [] ) return ( <FlatList ref={ref} data={posts} keyExtractor={keyExtractor} renderItem={renderItem} onEndReached={onEndReached} onEndReachedThreshold={0.6} showsVerticalScrollIndicator={false} removeClippedSubviews /> ) // Post Component const Post = ({ post }) => { return ( <View style={styles.post}> <PostHeader /> <View style={styles.postBody}> <PostContent /> <PostFooter /> </View> </View> ) } const isEqual = (prevProps, nextProps) => { return prevProps.post.id === nextProps.post.id } export default React.memo(Post, isEqual) 函数上使用了 useCallback,还在 renderItem 的导出中使用了 React.memo - 但是,{{1} } 仍然在状态改变时呈现每个帖子项。

我在这里遗漏了什么?

3 个答案:

答案 0 :(得分:1)

我想知道您是如何使用 keyExtractor 功能的。从 here 可以看出,在反应协调算法中,key 起着重要的作用。 如果你的帖子组件每次都被渲染,请考虑以下情况。

keyExtractor 的使用

如果 key 在同级中是唯一的,并且在渲染过程中保持不变,React 将不会使用与以前的帖子(在以前的渲染中)相同的 key 重绘帖子。如果 key 不是唯一的,就会出现意想不到的结果,如果 key 每次渲染都在改变,所有的帖子都会重新渲染。

在您的代码中,keyExtractor 结合了 id 和索引。如果邮政订单改变了怎么办?如果它的顺序与以前不同,即使对于同一个帖子,key 也会不同。这可能是一个问题。如果您的帖子 id 未更改且在帖子中是唯一的,则可以使用 id 作为键。这是查找密钥的常用方法。

条件渲染

我不知道你的 FlatList 组件实现细节。如果此组件在某些情况下绘制帖子而在其他情况下不绘制,React 会重新渲染所有帖子,即使它们具有唯一且未更改的键。

可能存在其他原因,但我希望这些可能的情况对您有所帮助。下面是我的抽象代码。

FlatList:
const { 
    data,
    keyExtractor,
    renderItem: Component,
    ...
} = props;

return (
    <>
        {data && data.map((post, index) => (
            <Component key={keyExtractor(post)} item={post} />
        ))}
    </>
)

// Posts Feed Screen
const renderItem = useCallback(
    ({ item }) => (
      <Post post={item} />
    ),
    []
)

const keyExtractor = useCallback(
    (item, index) => `${item.id}`,
    []
  )

答案 1 :(得分:0)

这个怎么样:

const [posts, setPosts] = useState([]);

const onEndReached = async () => {
    await MyApi().getPosts()
    .then(newPosts => {
        setPosts([...posts, ...newPosts]);
    })
}

答案 2 :(得分:0)

我将发表我的评论作为答案,因为最终问题就是这样解决的:

这实际上比看起来更简单。发生的情况是,每次创建新引用时,当您将“引用类型”作为 props 传递时,组件将重新渲染。

因此,在更新“帖子”的减速器中,您应该确保不是每次都创建一个新列表,而是仅更新列表中单个帖子的属性。

例如,避免传播运算符。此外,post 组件不应将 post 作为 prop 而是实际上是 post 的 Id 并使用 useSelector 找到它本身,而 useSelector 又应返回属性的值。

因此,例如,每个显示的属性都有一个 useSelector 比整个帖子的单个 useSelector 更好。 原因是react渲染引擎比较使用“==”来发现差异并决定如何渲染。

并且两个对象可以逐个属性进行完全相同的比较,但由于它们是不同的实例而返回不同。

这里的关键是只更新reducer内部需要更新的属性,同时确保在useSelectors中返回值类型。