React Native FlatList已Memo化仍全量重渲染的优化问题
React Native FlatList 重渲染问题解决思路
问题情况
我碰到React Native里FlatList的重渲染问题:父组件的selectedNoteId state变化时,哪怕已经用memo封装了FlatList,所有列表项还是会跟着重渲染。我只想让被点击的那个TouchableOpacity对应的列表项重渲染,就算给memo传恒为true的比较函数,问题还是存在。
相关代码:
export default ({ notes }: Props) => { const noteSrv = new noteService() const [selectedNoteId, setSelectedNoteId] = useState(NaN) const noteSelected = (note: Note) => { setSelectedNoteId(note.id) } const ListMemo = memo(function ({ notes, isSelected, }: { notes: Note[] isSelected: boolean }) { return ( <FlatList data={notes} renderItem={(i) => ( <ListItem isSelected={false} note={i.item} noteSelected={noteSelected} /> )} keyExtractor={(item) => item.id.toString()} ></FlatList> ) }) return //whatever }
问题根源
ListMemo定义位置错误:把ListMemo放在父组件内部,每次父组件重渲染都会创建一个全新的组件类型,memo的缓存完全失效。noteSelected函数无缓存:父组件state变化时,noteSelected会重新生成,作为props传给ListItem后,即使ListItem用了memo,也会因为函数引用变化触发重渲染。isSelected逻辑错误:给所有ListItem传的isSelected都是false,完全没利用selectedNoteId判断当前项是否选中,逻辑本身不成立。
修复步骤及代码
1. 封装ListItem并缓存回调
先给ListItem加上memo,避免不必要的重渲染:
// 单独封装ListItem并使用memo const ListItem = memo(({ note, isSelected, noteSelected }: { note: Note isSelected: boolean noteSelected: (note: Note) => void }) => { return ( <TouchableOpacity onPress={() => noteSelected(note)}> {/* 根据isSelected渲染选中样式,比如背景色变化 */} <Text>{note.title}</Text> </TouchableOpacity> ) })
2. 把ListMemo移到父组件外部
确保组件类型稳定,让memo能正常生效:
// 移到父组件外部,避免每次父渲染生成新组件 const ListMemo = memo(function ({ notes, selectedNoteId, noteSelected, }: { notes: Note[] selectedNoteId: number | typeof NaN noteSelected: (note: Note) => void }) { return ( <FlatList data={notes} renderItem={(i) => ( <ListItem // 正确判断当前项是否选中 isSelected={i.item.id === selectedNoteId} note={i.item} noteSelected={noteSelected} /> )} keyExtractor={(item) => item.id.toString()} /> ) })
3. 父组件优化
用useCallback缓存noteSelected,避免函数引用频繁变化:
export default ({ notes }: Props) => { const noteSrv = new noteService() const [selectedNoteId, setSelectedNoteId] = useState(NaN) // 用useCallback缓存函数,依赖项只加setSelectedNoteId(React的state setter是稳定引用) const noteSelected = useCallback((note: Note) => { setSelectedNoteId(note.id) }, [setSelectedNoteId]) // 如果notes是父组件传来的可变数组,建议用useMemo缓存 const cachedNotes = useMemo(() => notes, [notes]) return ( <ListMemo notes={cachedNotes} selectedNoteId={selectedNoteId} noteSelected={noteSelected} /> ) }
额外提醒
- 不要在组件内部定义另一个组件,否则每次渲染都会生成新组件,
memo完全起不到作用。 - 如果
notes数组会频繁更新,一定要用useMemo缓存,避免因为数组引用变化触发FlatList的重渲染。
内容的提问来源于stack exchange,提问作者Meghdad
相关产品推荐
相关产品推荐

