UNPKG

3.15 kBPlain TextView Raw
1import React from "react"
2import { patch, newSymbol } from "./utils/utils"
3
4type Disposer = () => void
5
6const protoStoreKey = newSymbol("disposeOnUnmountProto")
7const instStoreKey = newSymbol("disposeOnUnmountInst")
8
9function runDisposersOnWillUnmount() {
10 ;[...(this[protoStoreKey] || []), ...(this[instStoreKey] || [])].forEach(propKeyOrFunction => {
11 const prop =
12 typeof propKeyOrFunction === "string" ? this[propKeyOrFunction] : propKeyOrFunction
13 if (prop !== undefined && prop !== null) {
14 if (Array.isArray(prop)) prop.map(f => f())
15 else prop()
16 }
17 })
18}
19
20export function disposeOnUnmount(target: React.Component<any, any>, propertyKey: PropertyKey): void
21export function disposeOnUnmount<TF extends Disposer | Array<Disposer>>(
22 target: React.Component<any, any>,
23 fn: TF
24): TF
25
26export function disposeOnUnmount(
27 target: React.Component<any, any>,
28 propertyKeyOrFunction: PropertyKey | Disposer | Array<Disposer>
29): PropertyKey | Disposer | Array<Disposer> | void {
30 if (Array.isArray(propertyKeyOrFunction)) {
31 return propertyKeyOrFunction.map(fn => disposeOnUnmount(target, fn))
32 }
33
34 const c = Object.getPrototypeOf(target).constructor
35 const c2 = Object.getPrototypeOf(target.constructor)
36 // Special case for react-hot-loader
37 const c3 = Object.getPrototypeOf(Object.getPrototypeOf(target))
38 if (
39 !(
40 c === React.Component ||
41 c === React.PureComponent ||
42 c2 === React.Component ||
43 c2 === React.PureComponent ||
44 c3 === React.Component ||
45 c3 === React.PureComponent
46 )
47 ) {
48 throw new Error(
49 "[mobx-react] disposeOnUnmount only supports direct subclasses of React.Component or React.PureComponent."
50 )
51 }
52
53 if (
54 typeof propertyKeyOrFunction !== "string" &&
55 typeof propertyKeyOrFunction !== "function" &&
56 !Array.isArray(propertyKeyOrFunction)
57 ) {
58 throw new Error(
59 "[mobx-react] disposeOnUnmount only works if the parameter is either a property key or a function."
60 )
61 }
62
63 // decorator's target is the prototype, so it doesn't have any instance properties like props
64 const isDecorator = typeof propertyKeyOrFunction === "string"
65
66 // add property key / function we want run (disposed) to the store
67 const componentWasAlreadyModified = !!target[protoStoreKey] || !!target[instStoreKey]
68 const store = isDecorator
69 ? // decorators are added to the prototype store
70 target[protoStoreKey] || (target[protoStoreKey] = [])
71 : // functions are added to the instance store
72 target[instStoreKey] || (target[instStoreKey] = [])
73
74 store.push(propertyKeyOrFunction)
75
76 // tweak the component class componentWillUnmount if not done already
77 if (!componentWasAlreadyModified) {
78 patch(target, "componentWillUnmount", runDisposersOnWillUnmount)
79 }
80
81 // return the disposer as is if invoked as a non decorator
82 if (typeof propertyKeyOrFunction !== "string") {
83 return propertyKeyOrFunction
84 }
85}