2

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?

0 Answers0