Reputation: 53
Let's assume I have a parent component in which I am displaying a chart component. This chart component takes in a time series data and plots it if the data satisfies a certain criteria, if not it passes an error message to the parent through a errorMessage state handler passed by parent to child.
Now if the time series does not validate the condition then the child would update the errorMessage which would cause re-rendering of the child, and would again lead to child executing the code and again updating the errorMessage leading to infinite loop.
Two questions here:
Refer to the pseudo-code below
const parent = () => {
const [error,setError] = useState(null);
return (
<div>
<Child data={[[1,2],[3,4],[4,5]]} onError={setError}/>
<ErrorDisplay error={error}/>
</div> );
}
const child = ({data, onError}) => {
if (!someCondition(data))
onError("There is some error")
return (
<Chart data={data}/>
);}
Upvotes: 1
Views: 84
Reputation: 1075239
As Gabriele Petrioli points out, provided nothing else changes, you won't be in an endless cycle because a state update that changes to exactly the same value doesn't cause a re-render. But by default you'd still get the child doing the work twice (once to find the error, then again when the parent re-renders to show the error and the child gets called again).
There are at least a couple of ways to avoid that duplication:
Hold the error state in Child
, not Parent
, and don't recompute error
if data
is unchanged.
Memo-ize the Child
so the function doesn't get called again when its props haven't changed.
Here's a version of #2 that relies on array identity (that is, it won't run Child
again if the same array is provided to it):
const Child = React.memo(({data, onError}) => {
if (!someCondition(data)) {
onError("There is some error")
return null; // Or whatever
}
return (
<Chart data={data}/>
);
});
A more defensive version could check to see whether the new array is equivalent to the previous one, even if they aren't the same array:
const childPropsAreEqual = (prevProps, currProps) => {
// Where `deepEquals` is a function that does a deep equivalence
// check on the array
return deepEquals(prevProps.data, currProps.data);
};
const Child = React.memo(({data, onError}) => {
if (!someCondition(data)) {
onError("There is some error")
return null; // Or whatever
}
return (
<Chart data={data}/>
);
}, childPropsAreEqual); // <== Note providing an "are equal" function
Upvotes: 2
Reputation: 196217
The reason you do not experience the loop is because of
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the Object.is comparison algorithm.)
Upvotes: 3