React性能优化:WebSocket高频更新下仅更新目标ComponentB组件避免批量重渲染的解决方案
Hey there, let's sort out this performance bottleneck you're hitting. The root issue here is that every time your WebSocket updates the data state in ComponentA, all 50 ComponentB instances get new props (even though only one needs to change), triggering a re-render for each. Here are practical, actionable solutions to fix this:
1. Restructure Your State to Target Specific ComponentB Instances
Instead of storing a single data value that gets passed to all ComponentBs, split your state to track which ComponentB needs an update and its corresponding data. This way, most ComponentBs won't receive changed props.
Modified ComponentA Code:
import { useState } from 'react'; export default function ComponentA(props) { // Store the target ComponentB ID and its specific data const [updates, setUpdates] = useState({ targetId: null, payload: {} }); function callBackFromWebsocket(jsonData) { // Decode the JSON to get the target ID and its data const { targetId, payload } = jsonData; // Only update the state with the specific component's data setUpdates({ targetId, payload }); } // ...Sarus WebSocket connection code here return ( <> {Array.from({ length: 50 }, (_, index) => { const componentId = String(index + 1); // Pass only the data relevant to this ComponentB (or undefined if not targeted) const componentData = updates.targetId === componentId ? updates.payload : undefined; return <ComponentB key={componentId} id={componentId} data={componentData} />; })} </> ); }
2. Wrap ComponentB with React.memo to Prevent Unnecessary Re-renders
Even with the state restructuring, you should use React.memo to memoize ComponentB. This tells React to skip re-rendering the component if its props haven't changed.
Modified ComponentB Code:
import { useEffect, memo } from 'react'; const ComponentB = memo((props) => { useEffect(() => { // Only run this if props.data changes (i.e., when this component is targeted) if (props.data) { // Execute your drawing/updating logic here } }, [props.data]); return ( // Your UI rendering logic here, using props.data only when it's present ); }); export default ComponentB;
3. Optional: Pass Only Relevant Data to Each ComponentB
If your payload contains data that's only needed for the target ComponentB, you can avoid passing any data to non-targeted components (as shown in the ComponentA example above). This makes the prop comparison in React.memo even more efficient, since undefined won't change unless the component becomes the target.
Why This Works:
- By restructuring the state to track only the target component and its data, most ComponentBs receive the same
undefined(or unchanged)dataprop on each update. React.memocompares the previous and current props of ComponentB; if they're identical, it skips re-rendering the component entirely.- This reduces the number of re-renders from 50 per WebSocket update to just 1, which will drastically improve performance with 100ms update intervals.
内容的提问来源于stack exchange,提问作者ReactJs newbie

