Why should addChangeListener be in componentDidMount instead of componentWillMount?

3.5k views Asked by At

I saw this line as an answer to another question on here:

"componentWillMount should be componentDidMount, or else you'll leak event emitters in node."

and I don't really understand it. Can someone explain with more detail?

More info:

Building a react application with flux, as part of the initial render, a child component computes some data. Ideally, after this data is computed, I would like to call an action that updates the store's state with a portion of this new data.

Normally, updating the store's state emits a change event that causes a re-render. However, because the change listener isn't being added until componentDidMount (rather than in componentWillMount), my top level component isn't able to listen for the change that occurs during the initial render and initiate a re-render.

If I move the addChangeListener to componentWillMount that would seem to fix this issue, but the above quote suggests that this is a bad idea?

2

There are 2 answers

2
damianmr On BEST ANSWER

Is hard to understand what that quote means without more context. What I can tell you is that there are huge differences between the two of those methods.

On one hand, componentWillMount is called before the component is actually added to the DOM. This is the last chance you have to update component's state and get it rendered before the component is rendered by the browser.

On the other hand, componentDidMount is called once the component has been attached to the DOM (the real one).

What you need really depends on your use case. In general, componentDidMount is used to integrate with other libraries (like jQuery), it provides a way to modify the HTML rendered by the component.

I suggest you to read these links:

2
Parakleta On

I think the prevailing wisdom that listeners should be set in componentDidMount because it prevents problems in isomorphic applications is a mistake. I think in 98% of cases for non-isomorphic applications setting listeners in either componentWillMount and componentDidMount will work the same way, but it is conceptually wrong and in the 2% of cases (such as the example given in the original question) it will do the wrong thing.

There are git issue discussions and comments in the React source code suggesting that it would be preferred that componentWillMount wasn't called on the server at all, but if it isn't then problems are created in the checksum test comparing the server prerender to the client initial render. Having componentWillMount on the server means that it isn't executed as part of the component lifecycle in this case, but this is being used as an excuse to not count it as part of the lifecycle in any case.

In fact, componentWillMount is exactly the right place to register listeners if you are not creating an isomorphic application. If you are creating an isomorphic application then you have to make some compromises due to the checksum/lifecycle issue not being ideal in this case (maybe just testing for the server environment and then not registering listeners?).

In non-isomorphic applications adding listeners in componentWillMount can save unnecessary re-renders in some cases and will register them in document order. The advantage of document order is that if you have a way to flush pending events as components are re-rendered (for example, takeRecords on a MutationObserver) then you can ensure the document is re-rendered top down instead of bottom up, converting the rendering complexity to linear from polynomial.

Additionally, there is no danger period between the initial render and when the listener is registered where the Store can change without triggering a render, causing the view to be out of sync with the Store (the example problem given in the original question). If the listener is registered in componentDidMount you either need to make sure the Store isn't changed in componentDidMount calls in children, or else force a re-render/re-sync after registering the listener, which if done in componentDidMount is done in reverse document order which may be polynomial complexity (depending on how/if the React setStates are aggregated).