Skip to content

Commit c96ef1a

Browse files
committed
Improved strict mode as discussed in mobxjs#798
1 parent b11ea69 commit c96ef1a

File tree

4 files changed

+52
-20
lines changed

4 files changed

+52
-20
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mobx",
3-
"version": "3.0.2",
3+
"version": "3.0.2-fix798-2",
44
"description": "Simple, scalable state management.",
55
"main": "lib/mobx.js",
66
"typings": "lib/mobx.d.ts",
@@ -77,4 +77,4 @@
7777
"state management",
7878
"data flow"
7979
]
80-
}
80+
}

src/core/derivation.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {IObservable, IDepTreeNode, addObserver, removeObserver} from "./observable";
2+
import {IAtom} from "./atom";
23
import {globalState} from "./globalstate";
34
import {fail} from "../utils/utils";
45
import {isComputedValue} from "./computedvalue";
@@ -102,19 +103,14 @@ export function isComputingDerivation() {
102103
return globalState.trackingDerivation !== null; // filter out actions inside computations
103104
}
104105

105-
export function checkIfStateModificationsAreAllowed(atom: IObservable) {
106-
if (globalState.allowStateChanges === false) {
107-
if (globalState.strictMode)
108-
fail(getMessage("m030") + atom.name);
109-
else
110-
fail(getMessage("m031") + atom.name);
111-
} else {
112-
// Observed observables should not be modified during a computation,
113-
// even not from inside an action!
114-
if (globalState.computationDepth > 0 && atom.observers.length > 0) {
115-
fail(getMessage("m027") + atom.name);
116-
}
117-
}
106+
export function checkIfStateModificationsAreAllowed(atom: IAtom) {
107+
// TODO: move to messages.ts
108+
// Should never be possible to change an observed observable from inside computed, see #798
109+
if (globalState.computationDepth > 0 && atom.observers.length > 0)
110+
fail(getMessage("m031") + atom.name);
111+
// Should not be possible to change observed state outside strict mode, except during initialization, see #563
112+
if (!globalState.allowStateChanges && globalState.strictMode && atom.observers.length > 0)
113+
fail(getMessage("m030") + atom.name);
118114
}
119115

120116
/**

src/utils/messages.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,10 @@ const messages = {
2525
"m024" : "whyRun() can only be used if a derivation is active, or by passing an computed value / reaction explicitly. If you invoked whyRun from inside a computation; the computation is currently suspended but re-evaluating because somebody requested its value." ,
2626
"m025" : "whyRun can only be used on reactions and computed values" ,
2727
"m026" : "`action` can only be invoked on functions" ,
28-
"m027" : "Computed values are not allowed to not cause side effects by changing observables that are already being observed. Tried to change: ",
2928
"m028" : "It is not allowed to set `useStrict` when a derivation is running" ,
3029
"m029" : "INTERNAL ERROR only onBecomeUnobserved shouldn't be called twice in a row" ,
31-
"m030" : "It is not allowed to create or change state outside an `action` when MobX is in strict mode. Wrap the current method in `action` if this state change is intended. Tried to change: " ,
32-
"m031" : "It is not allowed to change the state when a computed value or transformer is being evaluated. Use 'autorun' to create reactive functions with side-effects. Tried to change: " ,
30+
"m030" : "Since strict-mode is enabled, changing observable values outside actions is not allowed. Please wrap the code in an `action` if this change is intended. Tried to modify: " ,
31+
"m031" : "Computed values are not allowed to not cause side effects by changing observables that are already being observed. Tried to modify: ",
3332
"m032" : "* This computation is suspended (not in use by any reaction) and won't run automatically.\n Didn't expect this computation to be suspended at this point?\n 1. Make sure this computation is used by a reaction (reaction, autorun, observer).\n 2. Check whether you are using this computation synchronously (in the same stack as they reaction that needs it).",
3433
"m033" : "`observe` doesn't support the fire immediately property for observable maps." ,
3534
"m034" : "`mobx.map` is deprecated, use `new ObservableMap` or `mobx.observable.map` instead" ,

test/strict-mode.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,45 @@
11
var test = require('tape');
22
var mobx = require('../');
33

4-
var strictError = /It is not allowed to create or change state outside an `action` when MobX is in strict mode. Wrap the current method in `action` if this state change is intended/;
4+
var strictError = /Since strict-mode is enabled, changing observable values outside actions is not allowed. Please wrap the code in an `action` if this change is intended. Tried to modify: /;
55

66
test('strict mode should not allow changes outside action', t => {
77
var a = mobx.observable(2);
88
t.equal(mobx.isStrictModeEnabled(), false)
99
mobx.useStrict(true);
1010
t.equal(mobx.isStrictModeEnabled(), true)
11+
12+
// allowed, a is not observed
13+
t.doesNotThrow(() => a.set(3), strictError);
14+
15+
var d = mobx.autorun(() => a.get())
16+
// not-allowed, a is observed
1117
t.throws(() => a.set(3), strictError);
18+
d()
19+
1220
mobx.useStrict(false);
1321
t.equal(mobx.isStrictModeEnabled(), false)
1422
a.set(4);
1523
t.equal(a.get(), 4);
1624
t.end();
1725
});
1826

19-
test('actions can modify state in strict mode', t => {
27+
test('actions can modify observed state in strict mode', t => {
28+
var a = mobx.observable(2);
29+
var d = mobx.autorun(() => a.get())
30+
31+
mobx.useStrict(true);
32+
mobx.action(() => {
33+
a.set(3);
34+
var b = mobx.observable(4);
35+
})();
36+
37+
mobx.useStrict(false);
38+
d()
39+
t.end();
40+
});
41+
42+
test('actions can modify non-observed state in strict mode', t => {
2043
var a = mobx.observable(2);
2144

2245
mobx.useStrict(true);
@@ -35,6 +58,10 @@ test('reactions cannot modify state in strict mode', t => {
3558
mobx.useStrict(true);
3659
mobx.extras.resetGlobalState(); // should preserve strict mode
3760

61+
var bd = mobx.autorun(() => {
62+
b.get(); // make sure it is observed
63+
})
64+
3865
var d = mobx.autorun(() => {
3966
t.throws(() => {
4067
a.get();
@@ -52,6 +79,8 @@ test('reactions cannot modify state in strict mode', t => {
5279
t.throws(() => a.set(5), strictError);
5380

5481
mobx.useStrict(false);
82+
d()
83+
bd()
5584
t.end();
5685
});
5786

@@ -60,6 +89,10 @@ test('action inside reaction in strict mode can modify state', t => {
6089
var a = mobx.observable(1);
6190
var b = mobx.observable(2);
6291

92+
var bd = mobx.autorun(() => {
93+
b.get(); // make sure it is observed
94+
})
95+
6396
mobx.useStrict(true);
6497
var act = mobx.action(() => b.set(b.get() + 1));
6598

@@ -81,6 +114,8 @@ test('action inside reaction in strict mode can modify state', t => {
81114
t.equal(b.get(), 4, "b should not be 55");
82115

83116
mobx.useStrict(false);
117+
bd()
118+
d()
84119
t.end();
85120
});
86121

@@ -140,6 +175,7 @@ test('can create objects in strict mode with action', t => {
140175

141176
test('strict mode checks', function(t) {
142177
var x = mobx.observable(3);
178+
var d = x.observe(function() {})
143179

144180
mobx.extras.allowStateChanges(false, function() {
145181
x.get();
@@ -156,5 +192,6 @@ test('strict mode checks', function(t) {
156192
});
157193

158194
mobx.extras.resetGlobalState();
195+
d()
159196
t.end();
160197
});

0 commit comments

Comments
 (0)