John Younan John Younan - 3 months ago 19
React JSX Question

render() is not being called on this.setState()

I have a simple Navigation link component separated in Container/Presentational classes (Smart/dumb) like so:

My presentational component:

import React, { PropTypes } from 'react';

class Navigation extends React.Component {

componentWillReceiveProps() {
console.log("here bro componentWillReceiveProps");
}

render() {
console.log(this.props);
var self = this;
return (
<div>
<ul>
{
this.props.items.map( (m, index) => {
var style = '';

console.log("index", index, "focus", self.props.focused);
if(self.props.focused == index){
style = 'focused';
}
return <li key={Math.random()} className={style} onClick={self.props.clickHandler.bind(this, index)}>{m}</li>;
})
}
</ul>

<p>Selected: {this.props.items[this.props.focused]}</p>
</div>
);
}
}

export default Navigation;


My container component:

import React, {PropTypes} from 'react';
import Nav from '../components/Navigation';

class NavigationContainer extends React.Component {

constructor(props) {
super(props);
this.state = {
focused : 0
};
}

clicked(index) {
this.setState({focused: index}, function () {
console.log("set to", this.state.focused);
});
}

render() {
return (
<Nav items={['Home', 'About', 'Contact Us']} clickHandler={this.clicked} focused={this.state.focused}/>
);
}
}

NavigationContainer.propTypes = {};
export default NavigationContainer;


When any of the items (home, contact us ...etc) are clicked and state is modified the render() method is not being called again and so the props passed the dumb components are the latest. My understanding is that when state is mutated then render() is called. What am i doing wrong here?

Answer

When you extend React.Component with ES2015 class syntax you need to bind your action handlers to a context of your class.

Try this:

render() {
        return (
            <Nav items={['Home', 'About', 'Contact Us']} clickHandler={index => this.clicked(index)} focused={this.state.focused}/>
        );
    }

Generally, it's better not to use arrow functions or bind methods inside render as it generates a new copy of the function on any render call. Move function declaration to the class constructor.

I personally prefer to use arrow functions as class properties in this case

class MyClass extends React.Component {

  handleClick = () => {
    // your logic
  };

  render() {
    return (
      <button onClick={this.handleClick}>Click me</button>
    );
  }
}

It's not a part of ES2015 specification but babel stage-0 preset supports this syntax

You can read more about context binding in React in this article