Asynchronous SetState
See original GitHub issueHi 👋
I was playing with immer and it looks like they have support for asynchronous producers.
Then I was looking at a way to have side effects in producers and update the draft accordingly to update zustand’s store but set
doesn’t like Promises.
So, I was wondering if having set
being able to resolve promises and update the state from the resolved value would be something interesting for zustand.
~I was able to make it work but I wonder if there were any drawbacks since I have to explicitely get()
the state at the moment of producing the next state. Is it similar to how set
receives the current state?~
EDIT: After a few tests I’ve noticed that calling setState
multiple times one after the other, doesn’t quite work since they are called synchronously so the draft
being passed in to both is of the shape of what get()
returns, as soon as the async producer resolves it doesn’t have the previous state update so it overrides it completely. To solve this, we’d have to use the state set
gets.
Reproduction: https://codesandbox.io/s/x3rnq036xp — You can see in the console that after the second update, the state has only 2 items instead of 3.
import produce from "immer";
import create from "zustand";
const fetchThing = () => Promise.resolve("thing");
const [, api] = create(set => {
- const setState = fn => set(produce(fn));
+ const setState = async fn => {
+ set(await produce(get(), fn));
+ };
return {
things: [],
addThing() {
+ setState(draft => {
+ draft.things.push('first thing');
+ });
+ // This will override the entire "things" slice whenever it resolves.
setState(async draft => {
draft.things.push(await fetchThing());
});
}
};
});
api.subscribe(console.log);
api.getState().addThing();
Thank you! I’m happy to contribute if people are interested in updating the behaviour of set
.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:5 (3 by maintainers)
Top GitHub Comments
@Magellol @JeremyRH immer can’t (and won’t) allow async state, but the store can still be transformed towards immer using middleware. The overhead of set remains, but at least it can mutate, and these sets then can then be async. See: https://codesandbox.io/s/5k77o8lzkn
The twitter discussion where this came up: https://twitter.com/0xca0a/status/1116390665607421952
Hi, thanks for the feedback!
So the problem is the immer draft is stale by the time you mutate it. Even if zustand resolved promises in
set
, it wouldn’t fix the issue because the resolved value would be stale state.The only way I can think of solving the issue is to await the async values before calling
set
: