I'm using React with zustand
and the immer
middleware of zustand
.
I use a custom class in the zustand store, and with immer I can update the state and the custom class instance in the state with mutable patterns. At most of times, everything works well.
However, I have an async
function in the custom class, which will make changes on the instance info, However zustand set()
method doesn't allow async function, how can I call the async
method in my custom class to update the state?
I use zustand
to manage user account login state, I create an accountInfoStore
with zustand
and immer
middleware like below
export const useAccountStore = create(immerMiddleware(
function (set) {
let storeInfo = {
accountData: new AccountData(),
async updateAccountDataFromStorage() {
set(function (state) {
state.accountData.fromStorage();
});
}
async updatePassword(){
let newpwd = await asyncRequestPassword();
set(function (state) {
state.accountData.password = newpwd;
})
}
}
})
);
accountData
is a custom class instance, the custom class code example is below
export class AccountData {
// Support immer middleware
[immerable] = true;
constructor(account: string | null, password: string | null) {
this.account = account;
this.password = password;
}
async fromStorage(): Promise<AccountData> {
let [account, password] = await asyncRequest();
this.account = account;
this.password = password;
}
}
In example above, updatePassword
method could work properly, the method itself is async
, however it first wait for the async data acquired, then call the set()
method synchronously, this pattern could solve some async update problem.
However in another method, updateAccountDataFromStorage
, since the fromStorage()
method is in the custom class instance accountData
in the state, so I need to access state
, so I need to call state.accountData.fromStorage()
, however, as mentioned above, set()
don't like async function, and this won't work, with err msg below:
TypeError: Cannot perform 'set' on a proxy that has been revoked at Proxy.fromStorage
So my question is, when using zustand
and immer
middleware, how can I call an async
function in the state which will have side effect to the state to update the state. In this example, I want to call state.accountData.fromStorage()
, this method is async
, and will update the value in accountData
Notice: I have tried the sync version of this pattern, that is call a sync method in the state which cause side effect, and in this case, the zustand and immer works properly, and the state is successfully updated
What I tried
I tried to put the update method outside the state, and use let curState = useAccountStore.getState()
to get the current state, then call curState.accountData.fromStorage()
, however this will cause error too
TypeError: Cannot assign to read only property 'account' of object '#<AccountData>
If I used the wrong way? Or should I just change my pattern to avoid depending an async
method's side effect in the state to update the current store?