使用useMemo渲染map元素时自定义组件重复渲染如何解决?
问题根因
- 核心原因是你用了条件渲染控制列表的显示/隐藏:当
loading变为true时,memoData对应的整个列表DOM会被直接卸载;3秒后先执行setLoading(false)让列表重新挂载,此时items还是旧值[1],所以会先渲染一次id为1的Item,紧接着执行setItems(newArray)更新列表,又渲染一次id为4的Item,就出现了两次打印。 - 次要问题:Item组件的
useEffect依赖整个props对象,浅比较下每次props引用变化都会触发副作用执行,即使id没有变化。
修正方案
- 把条件渲染改成样式控制显示隐藏,避免列表组件被反复卸载挂载:
把原来的条件渲染代码:
<div>{loading ? <label>{"Loading"}</label> : <div>{memoData}</div>}</div>
改成用display控制显隐:
<div> <label style={{display: loading ? 'block' : 'none'}}>Loading</label> <div style={{display: loading ? 'none' : 'block'}}>{memoData}</div> </div>
- 优化Item组件的useEffect依赖,只依赖实际用到的
id属性,避免不必要的副作用触发:
useEffect(() => { // call api with props.id }, [props.id]);
- 可选优化:如果后续要扩展渲染逻辑,把
renderItems用useCallback包裹,同时添加到useMemo的依赖数组中,符合React hook的依赖规则:
const renderItems = useCallback((item, index) => { return ( <div key={item}> <Item id={item}></Item> </div> ); }, []); const memoData = useMemo(() => { return <div>{items.map(renderItems)}</div>; }, [items, renderItems]);
如果是React18之前的版本,setTimeout异步回调中的两次状态更新不会自动批处理,也可以把setLoading(false)和setItems(newArray)用ReactDOM.unstable_batchedUpdates包裹,或者把loading和items合并为同一个state对象一次性更新,也能避免两次重渲染。
内容的提问来源于stack exchange,提问作者skyshine
相关产品推荐
相关产品推荐

