Asynchronous handlers
In most situations, you will have actions that run synchronously and directly update
changed atoms. However, you can also have actions that run asynchronously and which
depend on other delayed logic before changed atoms are updated. If you just need to
run asynchronous logic and update all atoms at the end, this is as easy as adding
the async
keyword to the action handler.
const useFetchUser = createSynergy(userDataAtom, bearerTokenAtom)
.createAction(() => async (userData, bearerToken) => {
const user = await fetchUser(bearerToken.current);
userData.current = user;
});
However, there are situations in which you want to update certain updates before the complete action has completed. You might be familiar with Hunks in Redux, which introduced a similar concept. There, you could dispatch other synchronous actions while handling one asynchronous hunk action, to update pieces of the state early before the handler has completed.
In synergies, this is done differently: Because you don't just return updated atom states at the end of the action, but rather update pieces of each atom state by writing to the draft state, the state of the draft will change between lines. You can now trigger an update to any of the atoms within the synergy to flush any changes made to the draft so far to subscribing components, even before completing the action. The handler will then continue to complete the remaining action, and flush remaining atom changes at the end.
const useFetchUser = createSynergy(userDataAtom, bearerTokenAtom, isLoadingAtom)
.createAction(() => async (userData, bearerToken, isLoading) => {
isLoading.current = true;
isLoading = isLoading.trigger();
const user = await fetchUser(bearerToken.current);
userData.current = user;
isLoading.current = false;
});
In this example, components subscribing to the isLoading
atom will be updated at the beginning, and
again alongside components subscribing to the userData
atom at the end. Note that, flushing an atom
state will also reset its change state, so if it is not changed again, it will not trigger a re-render
at the end again.
const useFetchUser = createSynergy(userDataAtom, isInitialRunAtom)
.createAction(() => async (userData, bearerToken, isInitialRun) => {
if (isInitialRun.current) return;
isInitialRun.current = true;
isInitialRun = isInitialRun.trigger(); // updates isInitialRunAtom
const user = await fetchUser();
userData.current = user;
// Will update userDataAtom, but not isInitialRunAtom; That was updated earlier.
});
caution
Always remember to reassign atom drafts to the return value of the trigger()
function.
Flushing an atom state will complete the draft, and it will not be able to be written to
again, so you need to use the fresh draft copy of the atom state returned by trigger()
to update it again.