React中如何更新状态数组映射的嵌套组件,避免全量重新渲染?
问题根因
你之前用React.memo不生效主要是两个常见问题:
- 每次
Board组件重渲染时,addCard、addList函数都会重新生成,引用地址变化,导致List组件接收的addCardprop每次都不同,React.memo的浅比较直接判定需要重渲染 - 更新
lists状态时如果做了全量深拷贝,所有列表对象的引用都会变化,哪怕内容没改的列表也会被判定为props变化,触发重渲染
无需引入Redux的优化方案
只用React自带的API就能解决,按以下步骤修改即可:
1. 用useCallback固定回调函数引用
给addList、addCard包裹useCallback,状态更新用函数式写法,不需要依赖外部lists变量,依赖项设为空数组,保证函数引用永远不变:
const addList = useCallback((listTitle) => { // 调用新增列表接口成功后更新状态 api.addList(listTitle).then(newList => { setLists(prev => [...prev, newList]) }) }, []) const addCard = useCallback((listId, cardText) => { // 调用新增卡片接口成功后更新状态 api.addCard(listId, cardText).then(newCard => { setLists(prevLists => { // 只修改目标列表的引用,其他列表保持原引用 return prevLists.map(list => { if (list.listId === listId) { return { ...list, cards: [...list.cards, newCard] } } return list }) }) }) }, [])
2. 组件添加React.memo
List和Card都包裹React.memo,注意修正你原来List组件里的笔误:遍历的变量应该是传入的cards不是listCards:
// List组件 const List = React.memo(({ title, cards, listId, addCard }) => { return ( <section> <h2>{title}</h2> <ul> {cards.map((card) => <Card key={card.cardId} text={card.cardText} />)} </ul> <AddElementPanel listId={listId} addElement={addCard} /> </section> ) }) // Card组件 const Card = React.memo(({ text }) => { return <li>{text}</li> })
优化效果
修改完成后:
- 新增列表时,只有新的列表会触发渲染,原有所有列表、卡片都不会重渲染
- 给某个列表新增卡片时,只有目标列表和新增的卡片会触发渲染,其他列表、该列表下原有卡片都不会重渲染
如果后续要做拖拽功能,也可以用同样的不可变更新逻辑,只修改移动涉及的两个列表的引用即可,不会触发全量重渲染。
内容的提问来源于stack exchange,提问作者jamescodes
相关产品推荐
相关产品推荐

