Skip to content
This repository has been archived by the owner on Dec 31, 2020. It is now read-only.

Decorator version of disposeOnUnmount now works with arrays (closes #637) #641

Closed
wants to merge 1 commit into from
Closed
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,14 @@ class SomeComponent extends React.Component {
// decorator version
@disposeOnUnmount
someReactionDisposer = reaction(...)

// decorator version with arrays
@disposeOnUnmount
someReactionDisposers = [
reaction(...),
reaction(...)
]


// function version over properties
someReactionDisposer = disposeOnUnmount(this, reaction(...))
Expand Down
43 changes: 25 additions & 18 deletions src/disposeOnUnmount.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ import { patch, newSymbol } from "./utils/utils"

const storeKey = newSymbol("disposeOnUnmount")

function checkFunc(prop) {
if (typeof prop !== "function") {
throw new Error(
"[mobx-react] disposeOnUnmount only works on functions such as disposers returned by reactions, autorun, etc."
)
}
}

function check(prop) {
if (Array.isArray(prop)) {
prop.map(checkFunc)
} else {
checkFunc(prop)
}
}

function runDisposersOnWillUnmount() {
if (!this[storeKey]) {
// when disposeOnUnmount is only set to some instances of a component it will still patch the prototype
Expand All @@ -12,45 +28,36 @@ function runDisposersOnWillUnmount() {
const prop =
typeof propKeyOrFunction === "string" ? this[propKeyOrFunction] : propKeyOrFunction
if (prop !== undefined && prop !== null) {
if (typeof prop !== "function") {
throw new Error(
"[mobx-react] disposeOnUnmount only works on functions such as disposers returned by reactions, autorun, etc."
)
}
prop()
check(prop)
if (Array.isArray(prop)) prop.map(f => f())
else prop()
}
})
this[storeKey] = []
}

export function disposeOnUnmount(target, propertyKeyOrFunction) {
if (Array.isArray(propertyKeyOrFunction)) {
return propertyKeyOrFunction.map(fn => disposeOnUnmount(target, fn))
}

export function disposeOnUnmount(target, propertyKeyOrFunctionOrArray) {
if (!target instanceof React.Component) {
throw new Error("[mobx-react] disposeOnUnmount only works on class based React components.")
}

if (typeof propertyKeyOrFunction !== "string" && typeof propertyKeyOrFunction !== "function") {
throw new Error(
"[mobx-react] disposeOnUnmount only works if the parameter is either a property key or a function."
)
if (typeof propertyKeyOrFunctionOrArray !== "string") {
check(propertyKeyOrFunctionOrArray)
}

// add property key / function we want run (disposed) to the store
const componentWasAlreadyModified = !!target[storeKey]
const store = target[storeKey] || (target[storeKey] = [])

store.push(propertyKeyOrFunction)
store.push(propertyKeyOrFunctionOrArray)

// tweak the component class componentWillUnmount if not done already
if (!componentWasAlreadyModified) {
patch(target, "componentWillUnmount", runDisposersOnWillUnmount)
}

// return the disposer as is if invoked as a non decorator
if (typeof propertyKeyOrFunction !== "string") {
return propertyKeyOrFunction
if (typeof propertyKeyOrFunctionOrArray !== "string") {
return propertyKeyOrFunctionOrArray
}
}
35 changes: 35 additions & 0 deletions test/disposeOnUnmount.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,3 +503,38 @@ it("componentDidMount should be different between components", async () => {
await doTest(true)
await doTest(false)
})

describe("should works with arrays", async () => {
test("as function", async () => {
class C extends React.Component {
methodA = jest.fn()
methodB = jest.fn()

componentDidMount() {
disposeOnUnmount(this, [this.methodA, this.methodB])
}

render() {
return null
}
}

await testComponent(C)
})

test("as decorator", async () => {
class C extends React.Component {
methodA = jest.fn()
methodB = jest.fn()

@disposeOnUnmount
disposers = [this.methodA, this.methodB]

render() {
return null
}
}

await testComponent(C)
})
})