MobX:由于启用了严格模式,因此不允许在不使用操作的情况下更改(观察到的)可观察值

问题描述 投票:0回答:3

我的上下文如下所示:

class AuthStoreClass {
    authUser = null

    constructor() {
        makeAutoObservable(this)
    }

    login = async (params) => {
        const { data: { data: authUser } } = await loginUser(params)
        this.authUser = authUser
    }
}

const AuthStoreContext = React.createContext(null);

export const authStoreObject = new AuthStoreClass()

export const AuthStoreProvider = ({ children }: any) => {
    return <AuthStoreContext.Provider value={authStoreObject}>{children}</AuthStoreContext.Provider>;
};
export const useAuthStore = () => {
    return React.useContext(AuthStoreContext);
};

我在组件的其他地方使用上下文:

const LoginPage = observer(() => {
    const authStore = useAuthStore()
    ...
    authStore.login(...)

最后一行报告以下警告:

[MobX] 由于启用了严格模式,因此不允许在不使用操作的情况下更改(观察到的)可观察值。尝试修改:[电子邮件受保护]

一切都按预期进行。我该如何解决这个问题?

javascript reactjs mobx mobx-react mobx-react-lite
3个回答
76
投票

您的

login
函数是
async
,您需要在内部使用
runInAction
,或者在单独的操作中处理结果,或者使用其他处理异步操作的方法:

import { runInAction, makeAutoObservable } from "mobx"

class AuthStoreClass {
    authUser = null

    constructor() {
        makeAutoObservable(this)
    }

    login = async (params) => {
        const { data: { data: authUser } } = await loginUser(params)
        
        // Wrap all changes with runInAction
        runInAction(() => {
          this.authUser = authUser
        })

        // or do it in separate function
        this.setUser(authUser)
    }

    // This method will be wrapped into `action` automatically by `makeAutoObservable`
    setUser = (user) => {
        this.authUser = user
    }
}

这是因为,引用文档,在异步过程中更新可观察量的每个步骤(“tick”)都应该标记为操作。第一个

await
之前的代码与
await
之后的代码处于不同的“刻度”中。

有关异步操作的更多信息(您甚至可以使用生成器!):https://mobx.js.org/actions.html#asynchronous-actions

在 MobX 版本 6 中,默认执行操作,但您可以使用

configure
方法禁用警告:

import { configure } from "mobx"

configure({
    enforceActions: "never",
})

但是要小心,

enforceActions
的目标是不要忘记将事件处理程序和所有突变包装在
action
中。不这样做可能会导致观察者额外重新运行。例如,如果您在某个处理程序中更改两个值而不采取任何操作,那么您的组件可能会重新渲染两次而不是一次。
makeAutoObservable
自动包装所有方法,但您仍然需要手动处理
async
方法和
Promises


3
投票

您还可以更改函数以使用

yield
语法,从而不再需要
runInAction

*login() {
    const { data: { data: authUser } } = yield loginUser(params)
    this.authUser = authUser
}


1
投票

另一种可能的解决方案是在类构造函数中定义方法并将其声明为

action

constructor() {
  makeAutoObservable(this, {
    login: action
  })
}

从“mobx”导入{action,makeAutoObservable};

class AuthStoreClass {
    authUser = null

    constructor() {
        makeAutoObservable(this, {
           login: action
        })
    }

    login = async (params) => {
        const { data: { data: authUser } } = await loginUser(params)
        this.authUser = authUser
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.