Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #3315: requiresObservable always takes precedence over global reactionRequiresObservable #3316

Merged
merged 1 commit into from
Feb 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/stale-melons-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"mobx": patch
---

`requiresObservable` always takes precedence over global `reactionRequiresObservable`
2 changes: 1 addition & 1 deletion docs/reactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Beyond that, there are also [`reaction`](#reaction) and [`when`](#when).

Usage:

- `autorun(effect: (reaction) => void)`
- `autorun(effect: (reaction) => void, options?)`

The `autorun` function accepts one function that should run every time anything it observes changes.
It also runs once when you create the `autorun` itself. It only responds to changes in observable state, things you have annotated `observable` or `computed`.
Expand Down
81 changes: 58 additions & 23 deletions packages/mobx/__tests__/v5/base/observables.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const {
const utils = require("../../v5/utils/test-utils")
const { MAX_SPLICE_SIZE } = require("../../../src/internal")

const voidObserver = function () { }
const voidObserver = function () {}

function buffer() {
const b = []
Expand Down Expand Up @@ -2100,7 +2100,7 @@ test("extendObservable should not accept complex objects as second argument", ()
})

test("observable ignores class instances #2579", () => {
class C { }
class C {}
const c = new C()
expect(observable(c)).toBe(c)
})
Expand All @@ -2121,9 +2121,9 @@ test("configure({ safeDescriptors: false })", () => {

class Clazz {
observable = 0
action() { }
get computed() { }
*flow() { }
action() {}
get computed() {}
*flow() {}
constructor() {
mobx.makeObservable(this, {
observable: mobx.observable,
Expand All @@ -2139,9 +2139,9 @@ test("configure({ safeDescriptors: false })", () => {

const plain = mobx.observable({
observable: 0,
action() { },
get computed() { },
*flow() { }
action() {},
get computed() {},
*flow() {}
})

checkDescriptors(plain)
Expand Down Expand Up @@ -2251,34 +2251,69 @@ test("ObservableArray.splice", () => {
})

describe("`requiresReaction` takes precedence over global `computedRequiresReaction`", () => {
let warnMsg = "[mobx] Computed value 'TestComputed' is being read outside a reactive context. Doing a full recompute.";
let consoleWarnSpy;
const name = "TestComputed"
let warnMsg = `[mobx] Computed value '${name}' is being read outside a reactive context. Doing a full recompute.`
let consoleWarnSpy
beforeEach(() => {
consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation()
})
afterEach(() => {
consoleWarnSpy.mockRestore()
mobx._resetGlobalState();
mobx._resetGlobalState()
})

test('`undefined`', () => {
test("`undefined`", () => {
mobx.configure({ computedRequiresReaction: true })
const c = mobx.computed(() => { }, { name: 'TestComputed' });
c.get();
expect(consoleWarnSpy).toHaveBeenLastCalledWith(warnMsg);
const c = mobx.computed(() => {}, { name })
c.get()
expect(consoleWarnSpy).toHaveBeenLastCalledWith(warnMsg)
})

test('`true` over `false`', () => {
test("`true` over `false`", () => {
mobx.configure({ computedRequiresReaction: false })
const c = mobx.computed(() => { }, { name: 'TestComputed', requiresReaction: true });
c.get();
expect(consoleWarnSpy).toHaveBeenLastCalledWith(warnMsg);
const c = mobx.computed(() => {}, { name, requiresReaction: true })
c.get()
expect(consoleWarnSpy).toHaveBeenLastCalledWith(warnMsg)
})

test('`false` over `true`', () => {
test("`false` over `true`", () => {
mobx.configure({ computedRequiresReaction: true })
const c = mobx.computed(() => { }, { name: 'TestComputed', requiresReaction: false });
c.get();
expect(consoleWarnSpy).not.toHaveBeenCalled();
const c = mobx.computed(() => {}, { name, requiresReaction: false })
c.get()
expect(consoleWarnSpy).not.toHaveBeenCalled()
})
})

describe("`requiresObservable` takes precedence over global `reactionRequiresObservable`", () => {
const name = "TestReaction"
let warnMsg = `[mobx] Derivation '${name}' is created/updated without reading any observable value.`
let consoleWarnSpy
beforeEach(() => {
consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation()
})
afterEach(() => {
consoleWarnSpy.mockRestore()
mobx._resetGlobalState()
})

test("`undefined`", () => {
mobx.configure({ reactionRequiresObservable: true })
const dispose = mobx.autorun(() => {}, { name })
dispose()
expect(consoleWarnSpy).toHaveBeenLastCalledWith(warnMsg)
})

test("`true` over `false`", () => {
mobx.configure({ reactionRequiresObservable: false })
const dispose = mobx.autorun(() => {}, { name, requiresObservable: true })
dispose()
expect(consoleWarnSpy).toHaveBeenLastCalledWith(warnMsg)
})

test("`false` over `true`", () => {
mobx.configure({ reactionRequiresObservable: true })
const dispose = mobx.autorun(() => {}, { name, requiresObservable: false })
dispose()
expect(consoleWarnSpy).not.toHaveBeenCalled()
})
})
6 changes: 5 additions & 1 deletion packages/mobx/src/core/derivation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,11 @@ function warnAboutDerivationWithoutDependencies(derivation: IDerivation) {
return
}

if (globalState.reactionRequiresObservable || derivation.requiresObservable_) {
if (
typeof derivation.requiresObservable_ === "boolean"
? derivation.requiresObservable_
: globalState.reactionRequiresObservable
) {
console.warn(
`[mobx] Derivation '${derivation.name_}' is created/updated without reading any observable value.`
)
Expand Down
4 changes: 2 additions & 2 deletions packages/mobx/src/core/reaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class Reaction implements IDerivation, IReactionPublic {
public name_: string = __DEV__ ? "Reaction@" + getNextId() : "Reaction",
private onInvalidate_: () => void,
private errorHandler_?: (error: any, derivation: IDerivation) => void,
public requiresObservable_ = false
public requiresObservable_?
) {}

onBecomeStale_() {
Expand Down Expand Up @@ -169,7 +169,7 @@ export class Reaction implements IDerivation, IReactionPublic {
if (!globalState.suppressReactionErrors) {
console.error(message, error)
/** If debugging brought you here, please, read the above message :-). Tnx! */
} else if (__DEV__) {console.warn(`[mobx] (error in reaction '${this.name_}' suppressed, fix error of causing action below)`)} // prettier-ignore
} else if (__DEV__) { console.warn(`[mobx] (error in reaction '${this.name_}' suppressed, fix error of causing action below)`) } // prettier-ignore

if (__DEV__ && isSpyEnabled()) {
spyReport({
Expand Down