UNPKG

3.99 kBPlain TextView Raw
1import React from "react"
2import { patch } from "./utils/utils"
3
4const reactMajorVersion = Number.parseInt(React.version.split(".")[0])
5let warnedAboutDisposeOnUnmountDeprecated = false
6
7type Disposer = () => void
8
9const protoStoreKey = Symbol("disposeOnUnmountProto")
10const instStoreKey = Symbol("disposeOnUnmountInst")
11
12function runDisposersOnWillUnmount() {
13 ;[...(this[protoStoreKey] || []), ...(this[instStoreKey] || [])].forEach(propKeyOrFunction => {
14 const prop =
15 typeof propKeyOrFunction === "string" ? this[propKeyOrFunction] : propKeyOrFunction
16 if (prop !== undefined && prop !== null) {
17 if (Array.isArray(prop)) prop.map(f => f())
18 else prop()
19 }
20 })
21}
22
23/**
24 * @deprecated `disposeOnUnmount` is not compatible with React 18 and higher.
25 */
26export function disposeOnUnmount(target: React.Component<any, any>, propertyKey: PropertyKey): void
27
28/**
29 * @deprecated `disposeOnUnmount` is not compatible with React 18 and higher.
30 */
31export function disposeOnUnmount<TF extends Disposer | Array<Disposer>>(
32 target: React.Component<any, any>,
33 fn: TF
34): TF
35
36/**
37 * @deprecated `disposeOnUnmount` is not compatible with React 18 and higher.
38 */
39export function disposeOnUnmount(
40 target: React.Component<any, any>,
41 propertyKeyOrFunction: PropertyKey | Disposer | Array<Disposer>
42): PropertyKey | Disposer | Array<Disposer> | void {
43 if (Array.isArray(propertyKeyOrFunction)) {
44 return propertyKeyOrFunction.map(fn => disposeOnUnmount(target, fn))
45 }
46
47 if (!warnedAboutDisposeOnUnmountDeprecated) {
48 if (reactMajorVersion >= 18) {
49 console.error(
50 "[mobx-react] disposeOnUnmount is not compatible with React 18 and higher. Don't use it."
51 )
52 } else {
53 console.warn(
54 "[mobx-react] disposeOnUnmount is deprecated. It won't work correctly with React 18 and higher."
55 )
56 }
57 warnedAboutDisposeOnUnmountDeprecated = true
58 }
59
60 const c = Object.getPrototypeOf(target).constructor
61 const c2 = Object.getPrototypeOf(target.constructor)
62 // Special case for react-hot-loader
63 const c3 = Object.getPrototypeOf(Object.getPrototypeOf(target))
64 if (
65 !(
66 c === React.Component ||
67 c === React.PureComponent ||
68 c2 === React.Component ||
69 c2 === React.PureComponent ||
70 c3 === React.Component ||
71 c3 === React.PureComponent
72 )
73 ) {
74 throw new Error(
75 "[mobx-react] disposeOnUnmount only supports direct subclasses of React.Component or React.PureComponent."
76 )
77 }
78
79 if (
80 typeof propertyKeyOrFunction !== "string" &&
81 typeof propertyKeyOrFunction !== "function" &&
82 !Array.isArray(propertyKeyOrFunction)
83 ) {
84 throw new Error(
85 "[mobx-react] disposeOnUnmount only works if the parameter is either a property key or a function."
86 )
87 }
88
89 // decorator's target is the prototype, so it doesn't have any instance properties like props
90 const isDecorator = typeof propertyKeyOrFunction === "string"
91
92 // add property key / function we want run (disposed) to the store
93 const componentWasAlreadyModified = !!target[protoStoreKey] || !!target[instStoreKey]
94 const store = isDecorator
95 ? // decorators are added to the prototype store
96 target[protoStoreKey] || (target[protoStoreKey] = [])
97 : // functions are added to the instance store
98 target[instStoreKey] || (target[instStoreKey] = [])
99
100 store.push(propertyKeyOrFunction)
101
102 // tweak the component class componentWillUnmount if not done already
103 if (!componentWasAlreadyModified) {
104 patch(target, "componentWillUnmount", runDisposersOnWillUnmount)
105 }
106
107 // return the disposer as is if invoked as a non decorator
108 if (typeof propertyKeyOrFunction !== "string") {
109 return propertyKeyOrFunction
110 }
111}