NOTICE: componentWillMount and this starter kit
ctrlplusb opened this issue ยท 8 comments
We use react-async-bootstrapper
to bootstrap your application on the client/server sides so that things like async components (and data in the features/react-jobs
branch) can be pre-resolved.
Your React application will go through the following process:
- Passed into
asyncBootstrapper
- this tries to mimic the behaviour of a server side render (to a degree) by walking your react component tree. It has to callcomponentWillMount
along the way as this is expected behaviour inrenderToString/renderToStaticMarkup
calls. Generally people put things like state initialisation etc in thecomponentWillMount
, so this will be needed so that therender
functions of your components can be called with the required state it needs to return it's result (i.e. children). The process continues down the "tree" until the bootstrapping process is complete. This may sound expensive but all we are doing is creating and throwing away object instances - no DOM creation/interaction/manipulation occurs. - After the bootstrapping process has complete your application is passed to the
render
/renderToString
functions for "normal" rendering (with all the global state given to it by the bootstrapping process).
I have found the above working awesomely for me in large production projects that use Redux as a state management tool. However, I have become aware of other libraries (e.g. MobX) that bind some "event listeners" within the componentWillMount
call. This will cause unexpected behaviour given our bootstrapping process. This is because the bootstrapping process never calls componentWillUnmount
, again mimicking the behaviour of a typical renderToString
/renderToStaticMarkup
call. Unfortunately libraries that generally do event binding in componentWillMount
do the unbinding within componentWillUnmount
. As we never call this the event listeners for the bootstrapping process live on, and then things like MobX state updates will cause things like forceUpdate
errors as the associated components do not exist.
I am considering extending the asyncBootstrapper
function to allow you to pass a prop stating that componentWillUnmount
should run. This may improve interoperability with these types of libraries, however, it could cause issues on others. We will have to experiment together.
For anyone concerned, @ctrlplusb has already made a patch that fixes the problem.
A boggling question from my perspective is... what is the side effect of calling componentWillUnmount by default for all components?
@birkir one thing I can think of is those components that rely/expect some sort of dom/event binding to exist. hopefully they check this is the case before doing any sort of disposal.
I do wrap the call with a try catch to avoid fallovers though. It will be interesting to see how this fairs for us.
hmm.. without setting the componentWillUnmount
flag in asyncBootstrapper
, the following also works:
useStaticRendering(true);
asyncBootstrapper(app).then(() => {
useStaticRendering(false);
render(app, content);
});
Hey @xiao-hu
I have never heard of useStaticRendering
- got any references on this?
Aha, just found that. Yeah, that sounds like a much better solution for mobx. Officially supported and you can use it around the full server side render too. ๐
https://github.com/mobxjs/mobx-react#server-side-rendering-with-usestaticrendering
That's right. Thanks for the very detailed description of the root cause!
Guys, after applying this to my production app using useStaticRendering
like @xiao-hu mentioned earlier is the best solution in my case
useStaticRendering(true);
asyncBootstrapper(app).then(() => {
useStaticRendering(false);
render(app, content);
});
Using options { componentWillUnmount: true }
on asyncBootstrapper
function still produce forceUpdate warning on console if you have component that changing state inside componentWillUnmount
event.
Thanks for getting back to use about this @diondirza