Consider the following React functional component called Test
:
function Test() {
const [data, setData] = React.useState(new Set());
function handleClick() {
data.add(42);
setData(data);
}
return (
<>
<button onClick={handleClick}>ADD 42</button>
<p>{data.has(42) ? 'in set' : 'not in set'}</p>
</>
);
}
The code above is affected by a common defect -- despite the intention of triggering a re-render via setData(data)
, nothing will happen in practice as React doesn't check the contents of what data
refers to, and setData(data)
doesn't change any state.
The behavior can be seen live here on StackBlitz.
One possible workaround to achieve the desired behavior is to use structuredClone
as follows:
function handleClick() {
data.add(42);
setData(structuredClone(data));
}
Performing a deep copy of the data
object will force React to perform a re-render. However, this solution performs an useless deep copy and it does not clearly communicate the intent of the developer.
What is the idiomatic way to force React to render again in a situation like the above?
For example, I would like something such as:
function handleClick() {
data.add(42);
markAsDirty(data);
}