-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Description
What problem does this address?
When using the setter provided by useEntityProp() in an async function and you need to set a complex object with a partial update, it is likely that the original value within the closure is stale. For example, when using it to get and set meta, which is an object, we need to provide the entire meta object to the setter just to update a single meta value:
const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta', postId );
const handleAsyncTask = async () => {
const result = await asyncTask();
setMeta( { ...meta, taskKey: result } );
};If the asyncTask() above takes long enough that other meta values have been updated in the interim, when setMeta() is finally called those other updates will be wiped out.
(Note that useCallback() does not help us here, and the setter already uses it under the hood)
This is a good overview of the stale closure issue in React: https://medium.com/@kristinethejohnson/i-need-closure-s-javascript-react-stale-closures-2881c5861303
What is your proposed solution?
The useEntityProp hook's return value includes a setter method as the second item of the array, which currently only accepts a static value to set in the current entity state. This setter should also accept an update function the same way that the setter returned by useState() does.
If you pass a function as
nextState, it will be treated as an updater function. It must be pure, should take the pending state as its only argument, and should return the next state.
Since the underlying editEntityRecord() mechanism fetches the current state to apply edits, it should be able to check for an updater function and provide the current state before applying the update.