React定时拉取数据场景下,如何仅重渲染变化的Station组件
如何避免React中Station组件的不必要重渲染
问题根源
每次从接口拉取数据后,你直接用新数组替换了stationsData状态,而新数组里的每个stationDataObj都是全新的对象引用——哪怕内容和之前完全一致,React也会认为Station组件的props发生了变化,从而触发所有Station的重渲染。
最优解决方案
我们可以通过组件缓存+精准数据对比来解决这个问题,分两步实现:
1. 用React.memo缓存Station组件
React.memo会对组件的props进行浅比较,只有当props真正变化时才会重新渲染组件。直接包裹Station组件:
// 改造Station组件,添加React.memo缓存 const Station = React.memo(({ stationData }) => { return ( <div> <div>{stationData.Name}</div> <div>{stationData.Status}</div> <div>{stationData.updateTime}</div> </div> ); });
但这里有个问题:如果每次接口返回的stationData都是新对象(即使内容和旧数据完全一致),浅比较会认为props变化了,依然会触发重渲染。这时候需要自定义比较逻辑。
2. 自定义props比较函数(可选,针对对象内容不变但引用变化的场景)
给React.memo传入第二个参数,精准对比stationData的实际内容:
// 自定义比较函数,只对比站点的关键属性 const areStationPropsEqual = (prevProps, nextProps) => { const prev = prevProps.stationData; const next = nextProps.stationData; return ( prev.Name === next.Name && prev.Status === next.Status && prev.updateTime === next.updateTime ); }; // 用自定义比较函数缓存Station组件 const Station = React.memo(({ stationData }) => { return ( <div> <div>{stationData.Name}</div> <div>{stationData.Status}</div> <div>{stationData.updateTime}</div> </div> ); }, areStationPropsEqual);
3. 优化数据更新逻辑(可选,进一步减少状态变化)
在更新stationsData时,复用未变化的站点对象引用,只替换真正有更新的对象:
useEffect(() => { const fetchStationsData = async () => { const response = await fetch(`/stationsInfo`); const newData = await response.json(); // 对比新旧数据,复用未变化的对象引用 const updatedData = stationsData.map(oldStation => { const matchedNewStation = newData.find(s => s.Name === oldStation.Name); if (!matchedNewStation) return oldStation; // 内容完全一致则复用旧对象,否则用新对象 if ( oldStation.Status === matchedNewStation.Status && oldStation.updateTime === matchedNewStation.updateTime ) { return oldStation; } return matchedNewStation; }); // 加入新增的站点 const newStations = newData.filter(station => !stationsData.some(old => old.Name === station.Name) ); setStationsData([...updatedData, ...newStations]); handleStationsDataChange(); }; fetchStationsData(); const fetchInterval = setInterval(fetchStationsData, intervalTime * 1000); return () => clearInterval(fetchInterval); }, [stationsData]); // 加入stationsData依赖,避免闭包导致的旧数据问题
额外补充:工具栏统计实现
在DisplayStations组件中直接计算状态统计即可:
function DisplayStations() { const [stationsData, setStationsData] = useState([]); // 计算运行/暂停的站点数量 const runningCount = stationsData.filter(s => s.Status === 'Run').length; const holdingCount = stationsData.filter(s => s.Status === 'Hold').length; // ...其他逻辑 return ( <Box> {/* 顶部工具栏 */} <div className="toolbar">Running: {runningCount}, Holding: {holdingCount}</div> {/* 站点列表 */} {stationsData.map((station) => ( <Station key={station.Name} stationData={station} /> ))} </Box> ); }
内容的提问来源于stack exchange,提问作者Omer Messer
相关产品推荐
相关产品推荐

