Skip to content

Commit 98733da

Browse files
committed
Extends the tree walker with an options paramater allowing you to define whether componentWillUnmount should be called or not.
1 parent 690373e commit 98733da

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

src/__tests__/index.test.js

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,20 +111,31 @@ describe('reactTreeWalker', () => {
111111
}
112112
}
113113

114-
const tree = <Baz />
115-
// eslint-disable-next-line no-unused-vars
116-
const visitor = (element, instance, context) => {
117-
if (instance) {
118-
return true
119-
}
120-
return true
121-
}
122-
return reactTreeWalker(tree, visitor).then(() => {
114+
return reactTreeWalker(<Baz />, () => true).then(() => {
123115
const expected = { foo: 'bar' }
124116
expect(actual).toMatchObject(expected)
125117
})
126118
})
127119

120+
it('calls componentWillUnmount and does not fail if it errors', () => {
121+
let called = true
122+
123+
class Baz extends Component {
124+
componentWillUnmount() {
125+
called = true
126+
throw new Error('An error during unmount')
127+
}
128+
129+
render() {
130+
return <div>foo</div>
131+
}
132+
}
133+
134+
return reactTreeWalker(<Baz />, () => true, null, { componentWillUnmount: true }).then(() => {
135+
expect(called).toBeTruthy()
136+
})
137+
})
138+
128139
it('getChildContext', () => {
129140
class Baz extends Component {
130141
props: { children?: any };
@@ -144,9 +155,7 @@ describe('reactTreeWalker', () => {
144155
Qux.contextTypes = { foo: React.PropTypes.string.isRequired }
145156

146157
const tree = <Baz><Qux /></Baz>
147-
// eslint-disable-next-line no-unused-vars
148-
const visitor = (element, instance, context) => undefined
149-
return reactTreeWalker(tree, visitor).then(() => {
158+
return reactTreeWalker(tree, () => true).then(() => {
150159
const expected = { foo: 'bar' }
151160
expect(actual).toMatchObject(expected)
152161
})

src/index.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
// eslint-disable-next-line import/no-extraneous-dependencies
88
import { Children } from 'react'
99

10+
const defaultOptions = {
11+
componentWillUnmount: false,
12+
}
13+
1014
// Lifted from https://github.com/sindresorhus/p-reduce
1115
// Thanks @sindresorhus!
1216
const pReduce = (iterable, reducer, initVal) =>
@@ -49,7 +53,7 @@ export const isPromise = x => x != null && typeof x.then === 'function'
4953
// Recurse an React Element tree, running visitor on each element.
5054
// If visitor returns `false`, don't call the element's render function
5155
// or recurse into its child elements
52-
export default function reactTreeWalker(element, visitor, context) {
56+
export default function reactTreeWalker(element, visitor, context, options = defaultOptions) {
5357
return new Promise((resolve) => {
5458
const doVisit = (getChildren, visitorResult, childContext, isChildren) => {
5559
const doTraverse = (shouldContinue) => {
@@ -129,7 +133,21 @@ export default function reactTreeWalker(element, visitor, context) {
129133
instance.componentWillMount()
130134
}
131135

132-
return instance.render()
136+
const children = instance.render()
137+
138+
if (options.componentWillUnmount && instance.componentWillUnmount) {
139+
try {
140+
instance.componentWillUnmount()
141+
} catch (err) {
142+
// This is an experimental feature, we don't want to break
143+
// the bootstrapping process, but lets warn the user it
144+
// occurred.
145+
console.warn('Error calling componentWillUnmount whilst walking your react tree')
146+
console.warn(err)
147+
}
148+
}
149+
150+
return children
133151
},
134152
visitor(element, instance, context),
135153
() =>

0 commit comments

Comments
 (0)