I have these two simplified React components, where the Times
component is a child of the Create
component (see below for code samples). The expected behavior is that initially, the Times
component is not shown, but when the user clicks on the link with onClick
, the Times
component appears.
The components work as expected for the most part, but strangely, after clicking on the onClick
link for the first time, the Times
component does not appear and the Create
component does not change state at all as is shown in the console. However, when clicking on the link a second time, the Create
component does change state and rerenders, and the Times
component 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'};
},
componentDidMount: function() {
if (this.props.shouldShow === false) {
this.setState({display: 'none'});
} else {
this.setState({display: 'block'});
}
},
componentWillReceiveProps: 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
component not being registered on the first click?
The problem is here.
componentWillReceiveProps: function() {
if (this.props.shouldShow === false) {
this.setState({display: 'none'});
} else {
this.setState({display: 'block'});
}
}
Which should be,
componentWillReceiveProps: function(newProps) {
if (newProps.shouldShow === false) {
this.setState({display: 'none'});
} else {
this.setState({display: 'block'});
}
}
ComponentWillRecieveProps is a member function of the component 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 component, so it makes sense to use props as much as possible and make them stateless.
There is a common 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 component intelligent. Using props in inner components, 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>
);
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With