'How to re-render a heavy React component with a low priority?
With some parameters, I have a React component that can take a few seconds to render but it is not a problem at all if it is not up-to-date.
To prevent the UI from being frozen, I would like to render it only when the user is not changing the form parameters.
It could be a defer, debounce, or another way of doing that. How would you achieve that? Thank you!
Solution 1:[1]
React 18
With React 18, a new API has been released that lets you mark state updates as less-important transition updates (as opposed to urgent updates)
Here's an example:
import {startTransition} from 'react';
// Urgent: Show what was typed
setInputValue(input);
// Mark any state updates inside as transitions
startTransition(() => {
// Transition: Show the results
setSearchQuery(input);
});
See https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-transitions
Another super helpful post with a real world example: https://github.com/reactwg/react-18/discussions/65
Solution 2:[2]
You could have an useEffect hook with no depedency array, so it updates every time the component is updated. On the hook, you'll start a timeout or interval to update the state of the component you want to lazy-render. At the return, you'll cancel the timer, which will be restarted at the next effect. That way, every time the form is updated, the rendering of the component is deferred.
Here is a codepen example that works as intended. There is a clock that always updates and the one in the button only updates when you're not interacting with it.
https://codepen.io/bernardofbbraga/pen/powdpqv
const element = <h1>Hello, world</h1>;
const Time = ({ time }) => new Date(time).toLocaleString();
const Clock = () => {
const timer = React.useRef(null);
const [time, setTime] = React.useState(Date.now());
React.useEffect(() => {
timer.current = setInterval(() => setTime(Date.now()), 1000);
return () => clearInterval(timer.current);
});
return <Time time={time} />;
};
const Lazy = () => {
const [counter, setCounter] = React.useState(1);
return (
<button onClick={() => setCounter(counter + 1)}>
{" "}
Counter:{counter}{" "}
<div>
This clock only updates when the user is not interacting with the button{" "}
<Clock />{" "}
</div>
</button>
);
};
ReactDOM.render(
<div>
App that updates frequently
<div>
<Clock />
</div>
<Lazy />
</div>,
document.getElementById("root")
);
Solution 3:[3]
You can use React.lazy and Suspense
to load the component on demand.
Here I use a flag loadExtra
to know when to load the extra component, offering a button to load it sooner than the 20 seconds
the timeout
is set for.
import { ..., lazy, Suspense } from "react";
const ExtraComponent = lazy(() => import("./ExtraComponent"));
const App = () => {
const [loadExtra, setLoadExtra] = useState(false);
...
<Suspense fallback="Loading...">
<ExtraComponent />
</Suspense>;
...
};
export default App;
I know of this trick, to force a Component
re-render. By passing the key
prop to the Component
, when the key changes, the component will be forced to re-render
So whenever you deem the user is not changing the form, you can change the key
and the React Component
will be re-rendered.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | merlindru |
Solution 2 | Bernardo Ferreira Bastos Braga |
Solution 3 |