You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The goal of enforceActions is that you don't forget to wrap event handlers in action.
Possible options:
"observed" (default): All state that is observed somewhere needs to be changed through actions. This is the default, and the recommended strictness mode in non-trivial applications.
"never": State can be changed from anywhere.
"always": State always needs to be changed through actions, which in practice also includes creation.
The benefit of "observed" is that it allows you to create observables outside of actions and modify them freely, as long as they aren't used anywhere yet.
Since state should in principle always be created from some event handlers, and event handlers should be wrapped, "always" captures this the best. But you probably don't want to use this mode in unit tests.
In the rare case where you create observables lazily, for example in a computed property, you can wrap the creation ad-hoc in an action using runInAction.
exportfunctioncheckIfStateModificationsAreAllowed(atom: IAtom){consthasObservers=atom.observers.length>0// Should never be possible to change an observed observable from inside computed, see #798if(globalState.computationDepth>0&&hasObservers)fail(process.env.NODE_ENV!=="production"&&`Computed values are not allowed to cause side effects by changing observables that are already being observed. Tried to modify: ${atom.name}`)// Should not be possible to change observed state outside strict mode, except during initialization, see #563if(!globalState.allowStateChanges&&(hasObservers||globalState.enforceActions==="strict"))fail(process.env.NODE_ENV!=="production"&&(globalState.enforceActions
? "Since strict-mode is enabled, changing observed observable values outside actions is not allowed. Please wrap the code in an `action` if this change is intended. Tried to modify: "
: "Side effects like changing state are not allowed at this point. Are you trying to modify state from, for example, the render function of a React component? Tried to modify: ")+atom.name)}
export functioncreateAction(actionName: string,fn: Function): Function&IAction{if(process.env.NODE_ENV!=="production"){invariant(typeoffn==="function","`action` can only be invoked on functions")if(typeofactionName!=="string"||!actionName)fail(`actions should have valid names, got: '${actionName}'`)}constres=function(){returnexecuteAction(actionName,fn,this,arguments)};(resasany).isMobxAction=truereturnresasany}
enforceActions作用
按照MobX的文档描述,enforceActions的目的是不要忘记给事件处理函数包裹actions。其取值分别为observed、never、always。
之所以推荐使用observed是因为在创建变量或者变量在使用是,允许自由更改。
主要注意的是,MobX4/5中默认的enforceActions是never,而MobX默认的enforceActions是observed。
MobX内部实现
以JDY项目中使用的MobX4为例。在Object.defineProperty中的set触发时,会触发到:
从上面的代码中可以得出:
如果
globalState.allowStateChanges === false
表示当前不允许修改state,如果enforce是严格模式('always')或者state已经存在监听(atom.observers.length > 0
)则会理解报错。我们可以同归下面的configure代码逻辑中佐证我们的想法。
我们以内部的runInAction为例,runInAction内部最终实现的的逻辑在:executeAction,而executeAction基本逻辑是:
其中startAction函数中通过调用allowStateChangesStart将globalState.allowStateChanges设置为true,而endAction中又将globalState.allowStateChanges恢复成原来的值。
实际工作中存在的问题
为什么下面代码会报错?
首先@action做了什么?action是一个decorator,其内部最终调用函数:createAction → executeAction
因为post中的处理函数是异步的,因此在执行时executeAction早已经执行了endAction,因此此时globalState.enforceActions是false。而@action的实现逻辑上
action.bound相比于action只是在createAction中将需要装饰的函数强制绑定了this。并没有其他的区别。
The text was updated successfully, but these errors were encountered: