The order is: child 1, child 2, child 3, parent
Technically though a child cannot be mounted in the DOM if the parent is not yet mounted.
I'd expect to see: parent, child 1, child 2, child 3
If I understand the docs correctly "componentDidMount" is meant for client-side only "state". I therefore use this to setup some "state", I have some child components which would need access to this "state" but can't cause it isn't initialized yet. I'm using "state" in quotes since it is not the React component state rather something external (websocket connection).
I could of course use other lifecycle callbacks to trigger the init but that would then execute on the server if I ever choose to use this feature.
componentWillMount
executes in the direction you expect (parent first).
Oh well, you wanted to not run this on server. Pardon.
That lifecycle method is only invoked once, only on the client, immediately after the initial rendering occurs. So no matter the order, the method isn't being called until after rendering.
You might want to use componentWillMount()
http://facebook.github.io/react/docs/component-specs.html#mounting-componentwillmount
That lifecycle method is only invoked once, only on the client, immediately after the initial rendering occurs.
That is what the docs say and exactly what I want. Why would I need a Websocket Connection on the server? It always starts disconnected and render should not wait for a connect, so whether it starts before or after rendering is not an issue.
Nevertheless, this is a bug in my opinion. Workarounds do exist though, so no big priority.
oh sorry, I guess I read your issue to quickly. Hmm, if the component really wasn't rendered at that point in the life cycle it would be the biggest issue that React has ever had, so I imagine it is.
Nevertheless, this is a bug in my opinion.
I don't think this is a bug.
By the time componentDidMount
fires, you expect component's DOM to be ready and that React wouldn't touch it until next componentWillUpdate
or componentWillReceiveProps
.
If componentDidMount
executed parent-first, the only thing you'd find there is your node, because all child nodes have not been mounted by this moment. This makes componentDidMount
useless for majority of use cases (e.g. counting offsetWidth
, attaching third-party non-React plugins, etc).
I'm only talking about the ORDER here.
I just re-read the docs and they never say whether the DOM element is actually in the document at this point only that it exist. So, I guess it is undefined which makes the order of the callbacks pretty much undefined as well.
Technically if componentDidMount
was defined as "dom element was created and added to the document" then the order is wrong, since a child cannot be in the DOM before its parent (it is a tree). The child can exist but in can't be in the document.
It seems to be defined this way since otherwise you could not reliably calculate dimensions or anything else that actually requires to DOM element to be in the document rather than just existing.
So, still a bug in my opinion.
var h1 = document.createElement('H1');
h1.innerText = 'hello world';
var div = document.createElement('DIV');
div.append(h1);
document.body.append(div);
Who entered the document first? div
or h1
?
EDIT: construct in the same order react would.
Yeah, good point. I think docs should be clarified. Waiting on expert opinion on this :-)
It's not a bug but maybe confusing docs. It's the creation order of the DOM elements. It has nothing to do with being in the document or not, since we cannot guarantee that anyway (since the root may be out of the document).
@thheller, what's your particular use case for the top-down order?
Technically the life-cycle methods only guarantee that your particular component is mounted. It doesn't make any promises with regard to it's children. Relying on this order is usually indication that you're breaking the encapsulation boundaries of components.
For example, if your component renders another component, that component may choose to delay rendering it's children while loading data (even if they're passed from above).
Technically the life-cycle methods only guarantee that your particular component is mounted. It doesn't make any promises with regard to it's children. Relying on this order is usually indication that you're breaking the encapsulation boundaries of components.
That's right. I was thinking more about relying on DOM “components” (i.e. if you have a ref, its node should be there by the time componentDidMount
happens).
var state = {};
function stateInit(id) {
state[id] = "dummy" // something more complex happening here
};
function stateSubscribe(id, channel, callback) {
if (state[id] == null) {
console.log("state doesn't exist yet", id);
}
};
var Inner = React.createClass({
componentDidMount: function() {
console.log("inner did mount", this.props);
stateSubscribe(this.props.con, this.props.channel, function(data) {});
},
render: function() {
return <h1>hello world</h1>;
}
});
var Outer = React.createClass({
componentDidMount: function() {
console.log("outer did mount", this.props);
stateInit(this.props.con);
},
render: function() {
return React.DOM.div(null,
Inner({con: this.props.id, channel: "1"}),
Inner({con: this.props.id, channel: "2"}),
Inner({con: this.props.id, channel: "3"})
);
}
});
React.render(<Outer con="connect-id"/>, document.body);
http://jsfiddle.net/thheller/rtbn2L0w/2/
Note that the actual code is ClojureScript and Om and this is only a minimal example of what I wanted to do. The websocket connection (state[id]
) may actually already exist without the Component and it will persist even if the component goes away, if it doesn't exist it must be initialized though.
There are plenty of other ways to organize this, my first was just using componentDidMount
and that didn't work out so well.
Given that it is just a misunderstanding on my part of what "mounted" means I will just use another method.
PS: You can actually figure out whether you are in the document or not quite simply. Just walk el.parentElement
till you either end at null
(false) or document.body
(true).
@thheller Yea, that use case seems very mutative in nature. You might be able to use componentWillMount
instead.
I was thinking more about relying on DOM “components” (i.e. if you have a ref, its node should be there by the time componentDidMount happens).
Technically, this is breaking the encapsulation of what a "DOM" parent does. It should be allowed to defer rendering it's children too. The direction we're going with first class refs, allow you to respond to a child's mount event, regardless if that happens later.
You can actually figure out whether you are in the document or not quite simply. Just walk el.parentElement till you either end at null (false) or document.body (true).
Yes, but you can't reliably and fast wait for it to become true (e.g. MutationObservers) cross-browser. We could throw if it's unmounted but that would hurt some use cases.
You might be able to use componentWillMount instead.
I think OP's original point was that componentWillMount
runs on server as well, but he only wants this behavior on client. I guess, he'd need to put this behind a flag similar to React's canUseDOM
.
Technically, this is breaking the encapsulation of what a "DOM" parent does. It should be allowed to defer rendering it's children too.
Interesting. Is this related to layout proposals?
Chose another implementation, still feels like a bug nonetheless.
Not familiar with React internals but doesn't it batch updates in a transaction? Checking whether a root element (no need to test the rest) is in the document or not should be a negligible cost.
But you don't really need to know I guess, the order sure isn't affected by it.
@thheller +1,
I agree that the order of execution has to be parent first. This is old, but I do think its quite relevant.
@sebmarkbage, A very common and very acceptable use case, is animations. (Note, I absolutely do not want to use the react-style libraries for that since it gets inefficient for complex animations - and you do want to squeeze the maximum performance out of the devices when it comes to mobile devices.)
So, consider the case below:
<Parent><Child/></Parent>
And parent's has componenDidMount
which, say strips the DOM, inserts its own elements, to place an artifical scroll bar (Now, this could be done in such a way that React needs to have no knowledge of it, where it doesn't affect its render) Now, I expect all my DOM elements to be rendered at this point, so I'm free to mutate my children as long as it doesn't affect my next render. And now, waking down the chain, I would expect the same thing in my children. But alas, the child will not see the changes in its componentDidMount
because it gets executed the other way. This doesn't make much sense for the componentDidMount
method, since for example, I cannot have access to the mutated DOM on the child to say, subscribe to the scroll events from its parent.
I have to jump through hoops and set up events to make this sane. This is highly intuitive. One would assume that the lifecycle hook ComponentDidMount
exists to interoperate with the DOM. (Since, speaking purely in an academic manner you wouldn't actually need it in a hypothetically world where everything is done the react way). And this order makes it unintuitive to do just that, so I'd highly recommend that this be considered as a bug, and the design choices re-evaluated.
PS: I'm only talking about the order of execution of the event, like @thheller mentioned before, not how the render happens, since the only logical way is to go from child to parent anyway there.
Similar issue:
// Items.jsx
...
componentDidMount () { eventHub.fire('trigger'); }
...
// Patent.jsx
...
componentDidMount () { eventHub.on('trigger'); }
...
I know some will tell me I've completely the wrong idea & I need 'to think in React', but the above is a very cropped example -- I shouldn't give up my soul just to have my Patent component mount before my Items component does.
This is surely a bug.
I just wandered into this thread looking for info about lifecycle fire order in order to manage some obnoxious focus stuff.
// Items.jsx
...
componentDidMount () { this.refs.input_1.focus() }
...
// Patent.jsx
...
componentDidMount () { this.refs.input_2.focus() }
...
what's focused right now? ¯_(ツ)_/¯
In my case I am including a 3rd party child component that does its own focus chicanery on didMount. My solution will probably involve an _.defer in the parent and work just fine, but it does illustrate a fun times fuzzy edge between react and the browser. First guess had been that mount order would be somehow deterministic.
Need to add to this here. We are using a framework that relies on bubbling events to communicate from children to parents.
Unfortunately, since a child's componentDidMount
gets called before a parent's, you cannot dispatch a bubbling event at that point and expect a parent to catch it.
So even if we cannot change componentDidMount
in the lifecycle, it would be nice to have a lifecycle method like "componentAttachedtoDOM` or something similar to indicate that the dom node has been fully attached to the DOM tree.
Hi, I am using Navigator and having the problem that componentDidMount
() is executed before ref on Navigator is executed, thus I can't access this._navigator
in componentDidMount()
.
I found the same issue on SO, but no answers so far. If you have an idea how to address this, it would be highly appreciated. Thank you!
Most helpful comment
It's not a bug but maybe confusing docs. It's the creation order of the DOM elements. It has nothing to do with being in the document or not, since we cannot guarantee that anyway (since the root may be out of the document).
@thheller, what's your particular use case for the top-down order?
Technically the life-cycle methods only guarantee that your particular component is mounted. It doesn't make any promises with regard to it's children. Relying on this order is usually indication that you're breaking the encapsulation boundaries of components.
For example, if your component renders another component, that component may choose to delay rendering it's children while loading data (even if they're passed from above).