Reputation: 43
Occasionally I may want to unmount and remount a component with new data inside it. This could look like:
setAllPosts(undefined);
setAllPosts(newArrayOfPosts);
Because React batches state changes, depending on where the newArrayOfPosts is coming from, the state won't change. I've been able to hack a solution with a setTimeout() of 1 second and then filling in setAllPosts(), but this feels so wrong.
Is there a best practice way to tell React to slow down for a moment? or maybe to not batch update this particular state change?
P.S. I know there are better ways to do this, but I am working inside a third party environment and am pretty limited to what I have access to.
Upvotes: 1
Views: 3289
Reputation: 169
This is how you can do it -
import { flushSync } from 'react-dom';
const handleClick = () => {
flushSync(() => {
setAllPosts(undefined);
// react will create a re-render here
});
flushSync(() => {
setAllPosts(newArrayOfPosts);
// react will create a re-render here
});
};
This is used to un-batch the react states. This is just a single way of doing it. The other way could be to use setTimeout
. Please note that with version 18 of react, state updates within setTimeouts are also being batched - this is known as Automatic Batching, but we still can achieve this by using different setTimeouts -
const handleClick = () => {
setTimeout(() => {
setAllPosts(undefined);
// react will create a re-render here
}, 10);
setTimeout(() => {
setAllPosts(newArrayOfPosts);
// react will create a re-render here
},20);
};
Just make sure to keep a time difference to rule out the batching done by React.
Upvotes: 3
Reputation: 85022
Once react 18 is available (it's currently a release-candidate) there will be a function that can force updates to not be batched: flushSync
import { flushSync } from 'react-dom';
flushSync(() => {
setAllPosts(undefined);
});
flushSync(() => {
setAllPosts(newArrayOfPosts);
});
Until then, you may need to do the setTimeout approach (though it doesn't need to be a whole second).
P.S. I know there are better ways to do this, but I am working inside a third party environment and am pretty limited to what I have access to.
Yeah, if you can do something else that would probably be better. Most of the time, if you want to deliberately unmount/remount a component, that is best achieved by using a key
which you change when you want the remount to happen.
const [key, setKey] = useState(0);
const [allPosts, setAllPosts] = useState([]);
// ...
setKey(prev => prev + 1);
setAllPosts(newArrayOfPosts);
// ...
return (
<SomeComponent key={key} posts={allPosts} />
)
Upvotes: 1
Reputation: 353
Occasionally I may want to unmount and remount a component with new data inside it.
It sounds like this use-case calls for a useEffect()
with a dependency based on something you care about, like another piece of state or prop being provided to this component.
useEffect(() => {
setAllPosts(newArrayOfPosts);
}, [shouldUpdate]);
I've even seen examples of people triggered useEffect()
with a dependency of a piece of state called count
or renderCount
. Not sure if this is necessarily best practice but it's one way to go about things.
const [count, setCount] = useState(0);
const [allPosts, setAllPosts] = useState([]);
useEffect(() => {
setAllPosts(props.values);
}, [count]);
const handleChange = () => {
setCount(prevCount => prevCount + 1); // This will trigger your useEffect when handleChange() in invoked
}
Upvotes: 0