I have these two simplified React ponents, where the Times
ponent is a child of the Create
ponent (see below for code samples). The expected behavior is that initially, the Times
ponent is not shown, but when the user clicks on the link with onClick
, the Times
ponent appears.
The ponents work as expected for the most part, but strangely, after clicking on the onClick
link for the first time, the Times
ponent does not appear and the Create
ponent does not change state at all as is shown in the console. However, when clicking on the link a second time, the Create
ponent does change state and rerenders, and the Times
ponent is seen.
Create.jsx
import React from 'react';
import Times from './Times.jsx';
export default React.createClass({
getInitialState: function () {
return {times: false};
},
_change: function () {
this.replaceState({times: true});
console.log(this.state.times);
},
render: function () {
return (
<div id="create">
<div id="outerbox">
<a onClick={this._change}>Times</a>
<Times shouldShow={this.state.times}/>
</div>
</div>
);
}
});
Times.jsx
import React from 'react';
export default React.createClass({
propTypes: {
shouldShow: React.PropTypes.bool.isRequired
},
getDefaultProps: function () {
return {shouldShow: false};
},
getInitialState: function () {
return {el: 'times'};
},
ponentDidMount: function() {
if (this.props.shouldShow === false) {
this.setState({display: 'none'});
} else {
this.setState({display: 'block'});
}
},
ponentWillReceiveProps: function() {
if (this.props.shouldShow === false) {
this.setState({display: 'none'});
} else {
this.setState({display: 'block'});
}
},
render: function () {
return (
<div id={this.state.el} style={{"display": this.state.display}}>
<h1>Times</h1>
</div>
);
}
});
Output of console.log
false
true
Why is the state of the Create
ponent not being registered on the first click?
I have these two simplified React ponents, where the Times
ponent is a child of the Create
ponent (see below for code samples). The expected behavior is that initially, the Times
ponent is not shown, but when the user clicks on the link with onClick
, the Times
ponent appears.
The ponents work as expected for the most part, but strangely, after clicking on the onClick
link for the first time, the Times
ponent does not appear and the Create
ponent does not change state at all as is shown in the console. However, when clicking on the link a second time, the Create
ponent does change state and rerenders, and the Times
ponent is seen.
Create.jsx
import React from 'react';
import Times from './Times.jsx';
export default React.createClass({
getInitialState: function () {
return {times: false};
},
_change: function () {
this.replaceState({times: true});
console.log(this.state.times);
},
render: function () {
return (
<div id="create">
<div id="outerbox">
<a onClick={this._change}>Times</a>
<Times shouldShow={this.state.times}/>
</div>
</div>
);
}
});
Times.jsx
import React from 'react';
export default React.createClass({
propTypes: {
shouldShow: React.PropTypes.bool.isRequired
},
getDefaultProps: function () {
return {shouldShow: false};
},
getInitialState: function () {
return {el: 'times'};
},
ponentDidMount: function() {
if (this.props.shouldShow === false) {
this.setState({display: 'none'});
} else {
this.setState({display: 'block'});
}
},
ponentWillReceiveProps: function() {
if (this.props.shouldShow === false) {
this.setState({display: 'none'});
} else {
this.setState({display: 'block'});
}
},
render: function () {
return (
<div id={this.state.el} style={{"display": this.state.display}}>
<h1>Times</h1>
</div>
);
}
});
Output of console.log
false
true
Why is the state of the Create
ponent not being registered on the first click?
1 Answer
Reset to default 17The problem is here.
ponentWillReceiveProps: function() {
if (this.props.shouldShow === false) {
this.setState({display: 'none'});
} else {
this.setState({display: 'block'});
}
}
Which should be,
ponentWillReceiveProps: function(newProps) {
if (newProps.shouldShow === false) {
this.setState({display: 'none'});
} else {
this.setState({display: 'block'});
}
}
ComponentWillRecieveProps is a member function of the ponent class that gets called just before props are "applied". Which means that this.props
are old Props. You should use newProps
which is served to the function as an argument.
http://codepen.io/bhargav175/pen/gaJmKz
Also, I get the feeling that you are using a little more state than you need. You dont need the display as a state variable at all. Times is an inner ponent, so it makes sense to use props as much as possible and make them stateless.
There is a mon misconception around that "props change don't affect the render, so lets use state". Props change results in rerender too, so always use props unless there is a strong reason and you really need to make an inner ponent intelligent. Using props in inner ponents, makes them dumb and easier to use.
http://codepen.io/bhargav175/pen/WQBpzP
var Times = React.createClass({
propTypes: {
shouldShow: React.PropTypes.bool.isRequired
},
getDefaultProps: function () {
return {shouldShow: false};
},
getInitialState: function () {
return {el: 'times'};
},
render: function () {
let display = this.props.shouldShow ? "block" : "none";
return (
<div id={this.state.el} style={{"display": display}}>
<h1>Times</h1>
</div>
);
}
});