React多次重新渲染后网站卡顿问题求助:随机Div条生成场景下的性能排查
Hey Adam, let's break down your problem and fix it step by step!
The core issue here is duplicate event listeners. Right now, you're running document.querySelector("#Reset").addEventListener directly in the top-level of your Bars component. That means every time the Bars component re-renders (which happens every time you call setBars), you're adding a new click listener to the Reset button.
After 12 clicks, you've got 12+ listeners all triggering setBars(getBars()) at the same time. This causes the component to re-render repeatedly, the DOM to update non-stop, and that's exactly why you're seeing severe lag.
Fix: Use useEffect to Bind Listeners Once
Wrap your event listener logic in a useEffect with an empty dependency array. This ensures the listener is only added once when the component mounts, and cleaned up when it unmounts:
function Bars() { const [bars, setBars] = React.useState(getBars()); React.useEffect(() => { const resetBtn = document.querySelector("#Reset"); const handleReset = () => setBars(getBars()); resetBtn.addEventListener("click", handleReset); // Cleanup: remove listener when component unmounts to avoid memory leaks return () => resetBtn.removeEventListener("click", handleReset); }, []); // Empty array = run only on mount/unmount return ( <div id="dataContainer"> <div id="barContainer"> {bars.map((height, index) => { // Important: Add a unique key for React to track elements efficiently return ( <div key={index} className="sBar" style={{ height: `${height}px` }}></div> ) })} </div> <h1>{bars.length} bars to sort</h1> <div id="btnContainer"><button id="btnSort">Sort</button></div> </div> ) }
Two quick extra fixes in the code above:
- Use
classNameinstead ofclassin JSX (it's React's required syntax) - Add a
keyprop to the mapped divs—this helps React update the DOM efficiently instead of re-rendering everything from scratch
Absolutely! Separating components by responsibility is a core React best practice—your menu handles UI controls, and Bars handles rendering the visualization. That's a great design choice.
But instead of using DOM queries to connect the components (which leads to issues like your lag), use React's built-in data flow patterns. Here's how to do it properly:
Better Approach: Lift State to a Parent Component
Move the bars state and reset logic up to a parent component, then pass the necessary data/functions down via props:
// Parent component that orchestrates state and child components function App() { const [bars, setBars] = React.useState(getBars()); const handleReset = () => setBars(getBars()); return ( <div> {/* Pass the reset function to your Menu component */} <Menu onReset={handleReset} /> {/* Pass the bars data to your Bars component */} <Bars bars={bars} /> </div> ) } // Menu component (now self-contained, no DOM queries needed) function Menu({ onReset }) { return ( <div> <button id="Reset" onClick={onReset}>Reset</button> {/* Add other menu items here */} </div> ) } // Bars component (focused solely on rendering) function Bars({ bars }) { return ( <div id="dataContainer"> <div id="barContainer"> {bars.map((height, index) => ( <div key={index} className="sBar" style={{ height: `${height}px` }}></div> ))} </div> <h1>{bars.length} bars to sort</h1> <div id="btnContainer"><button id="btnSort">Sort</button></div> </div> ) }
This approach has several benefits:
- No more DOM manipulation hacks—everything follows React's predictable data flow
- Components are decoupled and easier to test individually
- You avoid the duplicate listener issue entirely, since the reset function is only defined once in the parent
The lag was caused by duplicate event listeners piling up with each render—fixing that with useEffect will immediately resolve the卡顿. And your component separation idea is solid; using props to connect components instead of DOM queries will make your code cleaner, more maintainable, and less prone to bugs.
内容的提问来源于stack exchange,提问作者Adam Kaminski

