Skip to content

Commit af64a0a

Browse files
authored
State version cleanup (#3764)
* fix: disable global state version usage to avoid footgun with fresh props not propagationg, fixes #3728 * chore: Added changeset * chore: cleanup global state version
1 parent a2db19e commit af64a0a

File tree

5 files changed

+5
-73
lines changed

5 files changed

+5
-73
lines changed

packages/mobx-react-lite/src/useObserver.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ const getServerSnapshot = () => {}
1313
type ObserverAdministration = {
1414
reaction: Reaction | null // also serves as disposed flag
1515
onStoreChange: Function | null // also serves as mounted flag
16-
// BC: we will use local state version if global isn't available.
17-
// It should behave as previous implementation - tearing is still present,
16+
// stateVersion that 'ticks' for every time the reaction fires
17+
// tearing is still present,
1818
// because there is no cross component synchronization,
1919
// but we can use `useSyncExternalStore` API.
20+
// TODO: optimize to use number?
2021
stateVersion: any
2122
name: string
2223
// These don't depend on state/props, therefore we can keep them here instead of `useCallback`

packages/mobx/__tests__/v5/base/observables.js

-37
Original file line numberDiff line numberDiff line change
@@ -2361,43 +2361,6 @@ describe("`requiresObservable` takes precedence over global `reactionRequiresObs
23612361
})
23622362
})
23632363

2364-
test("state version updates correctly", () => {
2365-
// This test was designed around the idea of updating version only at the end of batch,
2366-
// which is NOT an implementation we've settled on, but the test is still valid.
2367-
2368-
// This test demonstrates that the version is correctly updated with each state mutations:
2369-
// 1. Even without wrapping mutation in batch explicitely.
2370-
// 2. Even in self-invoking recursive derivation.
2371-
const o = mobx.observable({ x: 0 })
2372-
let prevStateVersion
2373-
2374-
const disposeAutorun = mobx.autorun(() => {
2375-
if (o.x === 5) {
2376-
disposeAutorun()
2377-
return
2378-
}
2379-
const currentStateVersion = getGlobalState().stateVersion
2380-
expect(prevStateVersion).not.toBe(currentStateVersion)
2381-
prevStateVersion = currentStateVersion
2382-
o.x++
2383-
})
2384-
2385-
// expect(o.x).toBe(4) is 1?
2386-
prevStateVersion = getGlobalState().stateVersion
2387-
o.x++
2388-
expect(o.x).toBe(5)
2389-
expect(prevStateVersion).not.toBe(getGlobalState().stateVersion)
2390-
})
2391-
2392-
test("Atom.reportChanged does not change state version when called from the batch the atom was created in", () => {
2393-
mobx.transaction(() => {
2394-
const prevStateVersion = getGlobalState().stateVersion
2395-
const atom = mobx.createAtom()
2396-
atom.reportChanged()
2397-
expect(prevStateVersion).toBe(getGlobalState().stateVersion)
2398-
})
2399-
})
2400-
24012364
test('Observables initialization does not violate `enforceActions: "always"`', () => {
24022365
const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => {})
24032366

packages/mobx/src/core/atom.ts

+2-17
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ import {
1111
propagateChanged,
1212
reportObserved,
1313
startBatch,
14-
Lambda,
15-
globalState
14+
Lambda
1615
} from "../internal"
1716

1817
export const $mobx = Symbol("mobx administration")
@@ -27,17 +26,14 @@ export class Atom implements IAtom {
2726
isBeingObserved_ = false
2827
observers_ = new Set<IDerivation>()
2928

30-
batchId_: number
3129
diffValue_ = 0
3230
lastAccessedBy_ = 0
3331
lowestObserverState_ = IDerivationState_.NOT_TRACKING_
3432
/**
3533
* Create a new atom. For debugging purposes it is recommended to give it a name.
3634
* The onBecomeObserved and onBecomeUnobserved callbacks can be used for resource management.
3735
*/
38-
constructor(public name_ = __DEV__ ? "Atom@" + getNextId() : "Atom") {
39-
this.batchId_ = globalState.inBatch ? globalState.batchId : NaN
40-
}
36+
constructor(public name_ = __DEV__ ? "Atom@" + getNextId() : "Atom") {}
4137

4238
// onBecomeObservedListeners
4339
public onBOL: Set<Lambda> | undefined
@@ -68,17 +64,6 @@ export class Atom implements IAtom {
6864
* Invoke this method _after_ this method has changed to signal mobx that all its observers should invalidate.
6965
*/
7066
public reportChanged() {
71-
if (!globalState.inBatch || this.batchId_ !== globalState.batchId) {
72-
// We could update state version only at the end of batch,
73-
// but we would still have to switch some global flag here to signal a change.
74-
globalState.stateVersion =
75-
globalState.stateVersion < Number.MAX_SAFE_INTEGER
76-
? globalState.stateVersion + 1
77-
: Number.MIN_SAFE_INTEGER
78-
// Avoids the possibility of hitting the same globalState.batchId when it cycled through all integers (necessary?)
79-
this.batchId_ = NaN
80-
}
81-
8267
startBatch()
8368
propagateChanged(this)
8469
endBatch()

packages/mobx/src/core/globalstate.ts

-11
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,6 @@ export class MobXGlobals {
6363
*/
6464
inBatch: number = 0
6565

66-
/**
67-
* ID of the latest batch. Used to suppress reportChanged of newly created atoms.
68-
* Note the value persists even after batch ended.
69-
*/
70-
batchId: number = Number.MIN_SAFE_INTEGER
71-
7266
/**
7367
* Observables that don't have observers anymore, and are about to be
7468
* suspended, unless somebody else accesses it in the same batch
@@ -156,11 +150,6 @@ export class MobXGlobals {
156150
* configurable: true
157151
*/
158152
safeDescriptors = true
159-
160-
/**
161-
* Changes with each state update, used by useSyncExternalStore
162-
*/
163-
stateVersion = Number.MIN_SAFE_INTEGER
164153
}
165154

166155
let canMergeGlobalState = true

packages/mobx/src/core/observable.ts

-6
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,6 @@ export function queueForUnobservation(observable: IObservable) {
104104
* Avoids unnecessary recalculations.
105105
*/
106106
export function startBatch() {
107-
if (globalState.inBatch === 0) {
108-
globalState.batchId =
109-
globalState.batchId < Number.MAX_SAFE_INTEGER
110-
? globalState.batchId + 1
111-
: Number.MIN_SAFE_INTEGER
112-
}
113107
globalState.inBatch++
114108
}
115109

0 commit comments

Comments
 (0)