我在一个redux reducer中有一个posts
数组,我通过FlatList
的onEndReached
属性更新它。问题是,每次状态更改时,都会重新呈现整个列表,而不是受影响的发布项目。
// 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)
正如你所看到的,我在renderItem
函数上使用useCallback
,在导出Post Component
时也使用React.memo
——然而,FlatList
仍然会在状态更改时渲染每个帖子项。
我在这里错过了什么?
我将发表我的评论作为答案,因为最终问题就是这样解决的:
这实际上比看上去更简单。发生的事情是,当你通过";参考类型";作为道具,每次创建新引用时,组件都会重新渲染。
所以在你的减速器中;帖子";您应该确保不是每次都创建一个新列表,而是只更新列表中单个帖子的属性。
例如,避免使用扩散运算符。此外,post组件不应该将post作为道具,而应该实际使用post的Id,并使用useSelector来查找它本身,useSelector反过来应该返回属性的值。
因此,例如,与其为整个帖子显示一个useSelector,不如为每个显示的属性显示一个useSelector。原因是react渲染引擎比较使用"=="找出差异并决定如何渲染。
两个对象可以是完全相同的比较属性,但由于它们是不同的实例,所以返回的属性不同。
这里的关键是只更新减速器内部需要更新的属性,同时确保在useSelectors中返回值类型。
我想知道您是如何使用keyExtractor函数的。在react调解算法中,key扮演着重要的角色,从这里可以看出。如果你的所有帖子组件每次都被渲染,请考虑以下情况。
使用密钥提取器
如果关键帧在同级中是唯一的,并且在整个渲染过程中保持不变,React不会使用与前一个帖子相同的关键帧(在前一个渲染中(重新绘制帖子。若关键帧不是唯一的,则会发生意外的结果,若关键帧在每次渲染时都发生更改,则会重新渲染所有帖子。
在代码中,keyExtractor将id和索引组合在一起。如果邮寄订单发生变化怎么办?即使对于同一个帖子,如果它的顺序和以前不一样,密钥也会不同。这可能是个问题。如果你的帖子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}`,
[]
)
这个怎么办:
const [posts, setPosts] = useState([]);
const onEndReached = async () => {
await MyApi().getPosts()
.then(newPosts => {
setPosts([...posts, ...newPosts]);
})
}