React点击时如何避免所有列表项重渲染?仅渲染变更项的问题排查与解决方案
解决React列表项不必要重新渲染的问题
我来帮你分析问题根源,一步步搞定这个性能优化问题:
问题核心
你尝试用React.memo和useCallback但没生效,主要是两个原因:
onClick函数引用不稳定:在App组件里,每次渲染都会生成新的onClick函数,导致每个ListItem接收的onClickprop都是全新引用——哪怕用了React.memo,浅比较时也会判定props变化,触发全量重渲染。activeId全局更新触发所有项重渲染:当activeIndex改变时,所有ListItem的activeIdprop都会同步更新,这也会让React.memo的浅比较失效,导致所有列表项重新渲染。
修复方案
我们从这两个点入手优化,具体步骤如下:
1. 用useCallback稳定onClick函数引用
在App.ts中导入useCallback,把点击函数包裹起来,确保它的引用在组件渲染时保持不变:
import React, { useState, useCallback } from "react"; import "./styles.css"; import ListItem from "./ListItem"; const items = [ { id: 1, text: "items1" }, { id: 2, text: "items2" }, { id: 3, text: "items3" }, { id: 4, text: "items4" }, { id: 5, text: "items5" }, { id: 6, text: "items6" }, { id: 7, text: "items7" } ]; export default function App() { const [activeIndex, setActiveIndex] = useState(1); // 用useCallback缓存函数,依赖项为空(setActiveIndex是React提供的稳定引用) const onClick = useCallback((newActiveIndex: number) => { setActiveIndex(newActiveIndex); }, []); return ( <div className="App"> {items.map(({ id, text }) => { // 父组件提前计算当前项的激活状态 const isActive = activeIndex === id; return ( <ListItem key={id} id={id} text={text} isActive={isActive} onClick={onClick} /> ); })} </div> ); }
2. 修改ListItem组件,添加React.memo优化
把ListItem的props从activeId改为isActive,并用React.memo包裹组件,让它只在props发生实质性变化时重新渲染:
import React from "react"; interface ListItemProps { id: number; isActive: boolean; // 替换原有的activeId text: string; onClick: (newActiveIndex: number) => void; } const ListItem: React.FC<ListItemProps> = ({ id, isActive, text, onClick }) => { const style = { marginRight: "42px", marginTop: "24px", color: isActive ? "green" : "red" }; console.log(`update id: ${id}`); return ( <div> <span style={style}>id: {id}</span> <span style={style}>is active: {isActive ? "active" : "inactive"}</span> <span style={style}>text: {text}</span> <button onClick={() => onClick(id)}>setActive</button> </div> ); }; // 用React.memo包裹组件,实现浅比较优化 export default React.memo(ListItem);
优化效果说明
useCallback让onClick函数的引用保持稳定,不会因为App组件渲染而重新创建,避免了因这个prop变化导致的不必要重渲染。- 父组件提前计算
isActive并传递给子组件,意味着每次只有之前激活的项(isActive从true变false)和新激活的项(isActive从false变true)会触发重渲染,其他项的props完全不变,React.memo会直接跳过它们的重渲染流程。
现在你点击按钮时,只会看到两个列表项的控制台输出,其他项不会被重新渲染啦!
内容的提问来源于stack exchange,提问作者harakiri
相关产品推荐
相关产品推荐

