Let's say I have a context provider CounterProvider
like so:
const CounterProvider = ({ children }) => {
const [data, setData] = useState({ count: 0 });
const setCount = count => {
typeof count === 'function' && (count = count(data.count));
setData({ ...data, count });
};
return (
<CounterContext.Provider value={{...data, setCount}}>
{children}
</CounterContext.Provider>
);
};
And a consumer XComp
:
const XComp = () => {
const {count, setCount} = useContext(CounterContext);
useEffect(() => setCount(count_prev => ++count_prev));
return <div>XComp</div>;
};
Now, if I use XComp
3 times (ex: *sample-xcomp-usage), CounterProvider
state will be {count: 1}
. So, it looks like only the first call of setData()
in CounterProvider
becomes effective.
Now the question(s):
- Why does only the first call to
setData()
seem to be effective? - How can I make every call to
setData()
effective?
*sample-xcomp-usage:
<InstanceCounterProvider>
<XComp />
<XComp />
<XComp />
</InstanceCounterProvider>
.............
Note: While making this edit, I just realized the "why" and "how".
Answer to question 1:
Component's state changes via setData()
are not necessarily applied immediately, rather stored for later update. So subsequent callee setData()
does not normally see the changes. Causing the override of stored data. That is why only the first setData()
seems to be effective.
Answer to question 2:
React provides an overload of setState()
that takes a callback
as the argument to produce the changed state. The callback is provided with stored data. So, it is necessary modify the setCount()
inside CountProvider
like so:
const setCount = count => {
setData(data_prev => {
typeof count === 'function' && (count = count(data_prev.count));
return { ...data_prev, count };
};
};