Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React component only changes state on second onClick event

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?

like image 614
LanceLafontaine Avatar asked Nov 24 '15 03:11

LanceLafontaine


1 Answers

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>
    );
  }
});
like image 144
Bhargav Ponnapalli Avatar answered Nov 01 '22 08:11

Bhargav Ponnapalli