27
loading...
This website collects cookies to deliver better user experience
render
phase. During this phase, React traverses the current
Fiber tree and creates a list of effects that need to be applied to the Fiber nodes. The current
tree reflects the state of the application that was used to render the UI. As React processes effects, it clones the current tree and performs these updates on the current
tree, resulting in a new workInProgress
tree. Once all updates are processed, React will flush the workInProgress
tree to the DOM and this will become the current
tree. If you're interested in more detail, I cover the React Reconciliation algorithm in a separate post.const MyComponent = React.memo(({ data }) => {
return (
<ul>
{data.map((n) => (
<li key={n}>{n}</li>
))}
</ul>
);
});
data
changes, then we need to re-render the component with the latest values so these changes are reflected on the screen. Since we know that the component's output is dependent on data
, if data
does not change, then there is no need to recalculate the output as that is also unchanged. This allows us to use React.memo
or React.PureComponent
.data
does not change. This is what I refer to as an unnecessary re-render.render()
method for that component.const ClickCounter = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<>
<button onClick={handleClick}>Update counter</button>
<Counter count={count} />
<MyComponent data={["A", "B", "C"]} />
</>
);
};
count
is updated, MyComponent
will be re-rendered, even if it is not dependent on count
. This is caused as you are passing a new array reference on each render.["A", "B", "C"] === ["A", "B", "C"]; // false
ClickCounter
component.const data = ["A", "B", "C"];
const ClickCounter = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<>
<button onClick={handleClick}>Update counter</button>
<Counter count={count} />
<MyComponent data={data} />
</>
);
};
MyComponent
whenever the user clicks on the button
to update count
. But how much faster is our application following this optimization? Most likely, the improvements are negligible. So does this mean you shouldn't bother optimizing your re-renders?const data = ["A", "B", "C"];
const Demo = () => {
const location = useLocation();
return (
<>
<span>{location.pathname}</span>
<ul>
<li>
<MyComponent data={["A", "B", "C"]} />
</li>
<li>
<MyComponent data={data} />
</li>
</ul>
</>
);
};
MyComponent
; one which re-renders on each render and one which is correctly optimized. The Demo
itself component will render whenever location
changes, using the useLocation
hook from React Router.MyComponent
, there is the premise for obscure bugs which would be hidden by causing the component to needlessly re-render.const MyComponent = memo(({ data }) => {
const location = window.location;
return (
<>
<span>{location.pathname}</span>
<ul>
{data.map((n) => (
<li key={n}>{n}</li>
))}
</ul>
</>
);
});
MyComponent
, the rendered output includes the location.pathname
, which would change whenever the URL changes. If the component does not re-render, then the updated location.pathname
would not be visible on the screen. As a result, the MyComponent
which needlessly re-renders would reflect this change on the next render, while the optimized component would not.const MyComponent = memo(({ data }) => {
const location = useLocation();
return (
<>
<span>{location.pathname}</span>
<ul>
{data.map((n) => (
<li key={n}>{n}</li>
))}
</ul>
</>
);
});
return <MyComponent data={{ title: "Title" }} />;
React.useMemo
. The latter is usually required if the prop is dependent on other prop or state variables.return (
<MyComponent
onClick={() => {
doSomething(a, b);
}}
/>
);
React.useCallback
which returns a memoized callback function.const onClickHandler = React.useCallback(() => {
doSomething(a, b);
}, [a, b]);
return <MyComponent onClick={onClickHandler} />;
const Demo = () => {
return <MyComponent header={<Header />} />;
};
const Header = <Header />;
const Demo = () => {
return <MyComponent header={Header} />;
};