Skip to content

Commit bd148fe

Browse files
committed
Fix #3492: throw warning when use class component and suspense
1 parent f0e066f commit bd148fe

File tree

4 files changed

+58
-3
lines changed

4 files changed

+58
-3
lines changed

.changeset/chilly-nails-own.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"mobx-react": patch
3+
---
4+
5+
Fix #3492: throw warning when use class component and suspense

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ export { useLocalObservable } from "./useLocalObservable"
1515
export { useLocalStore } from "./useLocalStore"
1616
export { useAsObservableSource } from "./useAsObservableSource"
1717
export { resetCleanupScheduleForTests as clearTimers } from "./utils/reactionCleanupTracking"
18+
export {
19+
addReactionToTrack,
20+
reactionToTrackSymbol,
21+
recordReactionAsCommitted
22+
} from "./utils/reactionCleanupTracking"
1823

1924
export function useObserver<T>(fn: () => T, baseComponentName: string = "observed"): T {
2025
if ("production" !== process.env.NODE_ENV) {

packages/mobx-react-lite/src/utils/reactionCleanupTracking.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { createReactionCleanupTrackingUsingFinalizationRegister } from "./create
33
import { createTimerBasedReactionCleanupTracking } from "./createTimerBasedReactionCleanupTracking"
44
export { IReactionTracking } from "./reactionCleanupTrackingCommon"
55

6+
export const reactionToTrackSymbol = Symbol.for("reactionToTrackSymbol")
7+
68
const {
79
addReactionToTrack,
810
recordReactionAsCommitted,

packages/mobx-react/src/observerClass.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PureComponent, Component } from "react"
1+
import { PureComponent, Component, createRef } from "react"
22
import {
33
createAtom,
44
_allowStateChanges,
@@ -10,6 +10,11 @@ import {
1010
import { isUsingStaticRendering } from "mobx-react-lite"
1111

1212
import { newSymbol, shallowEqual, setHiddenProp, patch } from "./utils/utils"
13+
import {
14+
addReactionToTrack,
15+
reactionToTrackSymbol,
16+
recordReactionAsCommitted
17+
} from "mobx-react-lite"
1318

1419
const mobxAdminProperty = $mobx || "$mobx" // BC
1520
const mobxObserverProperty = newSymbol("isMobXReactObserver")
@@ -66,14 +71,40 @@ export function makeClassComponentObserver(
6671
)
6772
}
6873
target.render = function () {
74+
if (!this[reactionToTrackSymbol]) {
75+
this[reactionToTrackSymbol] = createRef()
76+
}
6977
this.render = isUsingStaticRendering()
7078
? originalRender
7179
: createReactiveRender.call(this, originalRender)
7280
return this.render()
7381
}
7482
patch(target, "componentDidMount", function () {
7583
this[mobxIsUnmounted] = false
76-
if (!this.render[mobxAdminProperty]) {
84+
recordReactionAsCommitted(this[reactionToTrackSymbol])
85+
let hasUpdated = false
86+
if (this[reactionToTrackSymbol].current) {
87+
this[reactionToTrackSymbol].current.mounted = true
88+
if (this[reactionToTrackSymbol].current.changedBeforeMount) {
89+
this[reactionToTrackSymbol].current.changedBeforeMount = false
90+
hasUpdated = true
91+
Component.prototype.forceUpdate.call(this)
92+
}
93+
!this.render[mobxAdminProperty]
94+
} else {
95+
const initialName = getDisplayName(this)
96+
this[reactionToTrackSymbol].current = {
97+
reaction: new Reaction(`${initialName}.render()`, () => {
98+
Component.prototype.forceUpdate.call(this)
99+
}),
100+
mounted: true,
101+
changedBeforeMount: false,
102+
cleanAt: Infinity
103+
}
104+
hasUpdated = true
105+
Component.prototype.forceUpdate.call(this)
106+
}
107+
if (!this.render[mobxAdminProperty] && !hasUpdated) {
77108
// Reaction is re-created automatically during render, but a component can re-mount and skip render #3395.
78109
// To re-create the reaction and re-subscribe to relevant observables we have to force an update.
79110
Component.prototype.forceUpdate.call(this)
@@ -84,6 +115,8 @@ export function makeClassComponentObserver(
84115
return
85116
}
86117

118+
this[reactionToTrackSymbol].current = null
119+
87120
const reaction = this.render[mobxAdminProperty]
88121
if (reaction) {
89122
reaction.dispose()
@@ -143,13 +176,18 @@ function createReactiveRender(originalRender: any) {
143176
try {
144177
setHiddenProp(this, isForcingUpdateKey, true)
145178
if (!this[skipRenderKey]) {
146-
Component.prototype.forceUpdate.call(this)
179+
if (this[reactionToTrackSymbol].current.mounted) {
180+
Component.prototype.forceUpdate.call(this)
181+
} else {
182+
this[reactionToTrackSymbol].current.changedBeforeMount = true
183+
}
147184
}
148185
hasError = false
149186
} finally {
150187
setHiddenProp(this, isForcingUpdateKey, false)
151188
if (hasError) {
152189
reaction.dispose()
190+
this[reactionToTrackSymbol].current = null
153191
// Forces reaction to be re-created on next render
154192
this.render[mobxAdminProperty] = null
155193
}
@@ -165,6 +203,11 @@ function createReactiveRender(originalRender: any) {
165203
isRenderingPending = false
166204
// Create reaction lazily to support re-mounting #3395
167205
const reaction = (reactiveRender[mobxAdminProperty] ??= createReaction())
206+
207+
if (!this[reactionToTrackSymbol].current) {
208+
addReactionToTrack(this[reactionToTrackSymbol], reaction, {})
209+
}
210+
168211
let exception: unknown = undefined
169212
let rendering = undefined
170213
reaction.track(() => {

0 commit comments

Comments
 (0)