It would be great if it was possible to run some code after the component actually unmounted. This is useful where you consider the following:
component Parent {
has service Service
has child Child
componentWillUnmount: {
destroys Service
}
render: {
Service gets passed to Child as prop
}
}
component Child {
componentWillMount: {
starts listening to Service
}
componentWillUnmount: {
stops listening to Service
}
}
The above will throw exception during Child componentWillUnmount since Parent gets unmounted first so Service is already destroyed.
If there existed componentDidUnmount the Service can be destroyed after the children are unmounted, i.e.
component Parent {
has service Service
has child Child
componentDidUnmount: {
destroys Service
}
render: {
Service gets passed to Child as prop
}
}
component Child {
componentWillMount: {
starts listening to Service
}
componentWillUnmount: {
stops listening to Service
}
}
That is an interesting use case, but I'm not yet convinced that it justifies an additional lifecycle method, because I think there is an equally viable alternative for your service deconstruction. I'm curious if you would agree...
What if a Service (as created by the parent) receives the information needed to open the required connection/resource, but doesn't actually open the resource until a child component starts listening. The service then closes the connection/resource when the last child stops listening. This means that the parent never needs to destroy the service, and normal garbage collection rules can apply. Would this approach work for you?
It's not really a Service per se, it's any object we use that follows the Observer pattern. It's just it would be good to have componentDidUnmount because then if Parent doesn't destroy the service then you're relying on Children to stop listening properly, which may cause leaks.
I think my previous comment above still holds true for most implementations of the Observer pattern. Just :%s/service/observable/g
If children don't stop listening properly, you've likely got bigger problems. It is common for data to be provided by a static parent and children to enter/exit throughout the lifetime of the application, so you'll likely be leaking anyway.
AFAIK, without a symmetrical hook for server-side rendering it is impossible to release any resources (unsubscribe listeners) used in componentWillMount
.
AFAIK, without a symmetrical hook for server-side rendering it is impossible to release any resources (unsubscribe listeners) used in componentWillMount.
Isn鈥檛 the current recommendation to never hold onto any resources in componentWillMount
and do this in componentDidMount
(which has a symmetric componentWillUnmount
to free them up)?
By the way I鈥檓 interested in having this method too.
When a child is subscribed to a Flux store, and the parent fires an action in componentWillUnmount
to clean up the store state, the child has to include special code for existence of this slice of state, or it would fail to retrieve it (potentially with a null reference).
The child doesn鈥檛 know that it should unsubscribe because the parent is cleaning up. There is no way for the parent to safely clean up any external state the child may depend upon. If we had componentDidUnmount
, the parent would be given this opportunity.
Yeah at the moment I had to include a bit ugly workarounds for the cases where componentDidUnmount
would help
@gaearon
The child doesn鈥檛 know that it should unsubscribe because the parent is cleaning up. There is no way for the parent to safely clean up any external state the child may depend upon. If we had componentDidUnmount, the parent would be given this opportunity.
@gaearon Yes, that was the original post/request almost verbatim. But is there a reason the approach in https://github.com/facebook/react/issues/6424#issuecomment-206436302 is insufficient? Seems like a parent can always create an object that lazy-initializes any required resource, and cleans it up when all users of those resources are done. Is it just that there is a little additional boilerplate? Is there a particular resource you have in mind that would exemplify the use case?
Is there a particular resource you have in mind that would exemplify the use case?
In this case, I鈥檓 referring to some piece of data in a Flux store that the parent cleans up. I don鈥檛 use this pattern myself but some consumers of React Redux bindings do, and I recently introduced a regression related to this use case (https://github.com/reactjs/react-redux/issues/351).
I wish I could tell people to use a different lifecycle hook for tearing down the data rather than being careful in the container implementation and having to implement workarounds like this: https://github.com/reactjs/react-redux/commit/ea53d6fb076359a864a1d543568d951d4b3eab3d. Asking them to pass something like onUnmount
also feels unnatural, especially considering that connected children can be deeply in the tree.
cc @sebmarkbage
By the way I briefly chatted with @sebmarkbage today about this, and my particular problem would be solved by delaying some calls until render()
or transactional setState()
callback. I didn鈥檛 use this pattern because setState()
itself is somewhat inefficient inside a large list, but this is a separate issue.
@jimfb
Seems like a parent can always create an object that lazy-initializes any required resource, and cleans it up when all users of those resources are done.
Unless the object cleans itself up (not ideal, and sometimes not available as an option, e.g. parent provides set of listeners (children), then listeners get unmounted, then parent provides another set of listeners), the parent can't clean the object up because the parent gets unmounted before the children.
Switching to Flux/Redux etc. is not for everyone.
Unless the object cleans itself up (not ideal, and sometimes not available as an option
Why is it not ideal? When is it not an option (I'm fairly certain it is always an option, because you can use simple reference counting to determine when to cleanup)?
e.g. parent provides set of listeners (children), then listeners get unmounted, then parent provides another set of listeners), the parent can't clean the object up because the parent gets unmounted before the children.
Why does the parent need to do any cleanup? Why doesn't the datastore cleanup when the last child unmounts?
@jimfb it's not ideal because you are basically re-encoding the logic for unmounting the tree of components from the deepest child up in your data type, which is just unnecessary error prone boilerplate.
I want to clean up my component after my children have unmounted - in principle this seems like a very reasonable thing to want to do, and I don't think it's reasonable to have to rebuild this lifecycle method externally.
Additionally, you do not always have the option of rewriting the data store - sometimes you are interfacing with legacy code! (This is my current personal situation)
We also need this method to be implemented
mega +1 for this one, the problem is perfectly explained by @gaearon.
The child doesn鈥檛 know that it should unsubscribe because the parent is cleaning up. There is no way for the parent to safely clean up any external state the child may depend upon. If we had componentDidUnmount, the parent would be given this opportunity.
I think I found a hacky way to do this by abusing the ref prop. When the ref prop is passed a function it calls this function passing you a ref to the dom element on mount and passing you null on unmount. By hijacking this function I was able to build a wrapper component that renders its children in a div and then an extra span/div element immediately after the children. The extra span/div has a ref prop set to a function that will bubble up to its parent if its called with a null (meaning the element has been unmounted). I then use this wrapper to sit between my parent and children components, using its onUnmount event in the parent to let me know after the children are gone so I can do the cleanup I need.
here is a link to an example and below is the code:
import React from 'react'
class ComponentChild extends React.Component {
componentWillUnmount() {
//console.log('Child Props', this.props)
const { label } = this.props
console.log('Unmonuting ' + label)
}
render () {
const { label } = this.props
return <h2>{label}</h2>
}
}
class UnloadWrapper extends React.Component {
constructor (props) {
super(props)
this.onUnload = this.onUnload.bind(this)
}
onUnload(c) {
if (!c) {
console.log('Children Unloaded!')
const { onUnload } = this.props
if (onUnload) {
onUnload()
}
}
}
render () {
const { children } = this.props
return (
<div>
{children}
<span ref={this.onUnload} />
</div>
)
}
}
class ComponentParent extends React.Component {
constructor(props) {
super(props)
this.state = {
hasChildren: false
}
}
render () {
return (
<div>
<button type="button" onClick={() => { this.setState({ hasChildren: !this.state.hasChildren })}}>Switch</button>
{this.state.hasChildren ?
<UnloadWrapper onUnload={this.onUnload}>
<ComponentChild label="Child 1" />
<ComponentChild label="Child 2" />
<ComponentChild label="Child 3" />
</UnloadWrapper> : null }
</div>
)
}
}
class Root extends React.Component {
render () {
return (
<ComponentParent />
)
}
}
export default Root
In my example code, the parent renders a button and a wrapped set of children that are mounted and unmounted on click. When the unmount happens you will see in the console that all three children's componentWillUnmount are called before the unload wrapper calls back to the parent, thus letting the parent know after its children have been fully removed.
So far this works but have not tested fully. My only concern is the order of the unmount sequence react does, I am assuming that on unmount, react processes each element, then loops children in order sequentially, which allows my unload wrapper to work. If it does anything an parallel then I might run into issues. If anybody can see if any major flaws in this please let me know!
The existing componentWillUnmount
lifecycle method is called for the parent first, and only then for each of it's children.
This makes it hard to dispose of shared resources created by the parent and used by the children, since the resources may still be in use by the children when the parent's cWU method is invoked.
Closing as React's design is moving away from class-based lifecycle methods
Most helpful comment
I think I found a hacky way to do this by abusing the ref prop. When the ref prop is passed a function it calls this function passing you a ref to the dom element on mount and passing you null on unmount. By hijacking this function I was able to build a wrapper component that renders its children in a div and then an extra span/div element immediately after the children. The extra span/div has a ref prop set to a function that will bubble up to its parent if its called with a null (meaning the element has been unmounted). I then use this wrapper to sit between my parent and children components, using its onUnmount event in the parent to let me know after the children are gone so I can do the cleanup I need.
here is a link to an example and below is the code:
In my example code, the parent renders a button and a wrapped set of children that are mounted and unmounted on click. When the unmount happens you will see in the console that all three children's componentWillUnmount are called before the unload wrapper calls back to the parent, thus letting the parent know after its children have been fully removed.
So far this works but have not tested fully. My only concern is the order of the unmount sequence react does, I am assuming that on unmount, react processes each element, then loops children in order sequentially, which allows my unload wrapper to work. If it does anything an parallel then I might run into issues. If anybody can see if any major flaws in this please let me know!