React: Why component's constructor is called only once?
ReactjsReactjs Problem Overview
In the following example, when Item 2
is clicked, Second 1
is shown instead of Second 2
. Why? How would you fix that?
var guid = 0;
class Content extends React.Component {
constructor() {
guid += 1;
this.id = guid;
console.log('ctor', this.id); // called only once with 1
}
render() {
return (
<div className="content">
{this.props.title} - {this.id}
</div>
);
}
}
class MyApp extends React.Component {
constructor() {
this.items = ['Item 1', 'Item 2'];
this.state = {
activeItem: this.items[0]
};
}
onItemClick(item) {
this.setState({
activeItem: item
});
}
renderMenu() {
return (
<div className="menu">
<div onClick={this.onItemClick.bind(this, 'Item 1')}>Item 1</div>
<div onClick={this.onItemClick.bind(this, 'Item 2')}>Item 2</div>
</div>
);
}
renderContent() {
if (this.state.activeItem === 'Item 1') {
return (
<Content title="First" />
);
} else {
return (
<Content title="Second" />
);
}
}
render() {
return (
<div>
{this.renderMenu()}
{this.renderContent()}
</div>
);
}
}
Reactjs Solutions
Solution 1 - Reactjs
React's reconciliation algorithm assumes that without any information to the contrary, if a custom component appears in the same place on subsequent renders, it's the same component as before, so reuses the previous instance rather than creating a new one.
If you were to implement componentWillReceiveProps(nextProps)
, you would see that getting called instead.
> ### Different Node Types
>
> It is very unlikely that a <Header>
element is going to generate a DOM that is going to look like what a <Content>
would generate. Instead of spending time trying to match those two structures, React just re-builds the tree from scratch.
>
> As a corollary, if there is a <Header>
element at the same position in two consecutive renders, you would expect to see a very similar structure and it is worth exploring it.
>
> ### Custom Components
>
> We decided that the two custom components are the same. Since components are stateful, we cannot just use the new component and call it a day. React takes all the attributes from the new component and calls component[Will/Did]ReceiveProps()
on the previous one.
>
> The previous component is now operational. Its render()
method is called and the diff algorithm restarts with the new result and the previous result.
If you give each component a unique key
prop, React can use the key
change to infer that the component has actually been substituted and will create a new one from scratch, giving it the full component lifecycle.
renderContent() {
if (this.state.activeItem === 'Item 1') {
return (
<Content title="First" key="first" />
);
} else {
return (
<Content title="Second" key="second" />
);
}
}