How to Set State with arguments passed to a function in React

georgeperry picture georgeperry · Feb 12, 2018 · Viewed 8.6k times · Source

I'm trying to pass an array (titles) from a child component to the parent, then set the state of the parent with the array. However, when handling the change in the increaseReads() method, I cannot change the articlesRead state

You will see two console.log() statements; the first one is successfully logging the titles but the second is logging an empty array - the previous state

The Child:

export class Publication extends React.Component {
  constructor() {
    super();
    this.state = {
      items: []
    };
  }

  componentDidMount() {
    fetch(this.props.url)
    .then(response => {
      return response.json();
    }).then(({ items })=> {
      this.setState({ items });
    });
  }

  handleClick () => {
    this.props.openArticle();
  }


  render() {
    return (
      <div className='publication'>
        <h4>{this.props.name}</h4>
        <ul>
          {this.state.items.map(item => (
           <li><a href={item.link} target='_blank' onClick={this.handleClick}>{item.title}</a></li>
          ))}
        </ul>
      </div>
    );
  }
}

The Parent:

export class Latest extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      totalReads: 0,
      articlesRead: []
    };
  }

  handleChange = () => {
    this.props.increaseTotal();
  }

  increaseReads(titles) {
    this.setState({
      totalReads: this.state.totalReads + 1,
      articlesRead: titles
    })

    // Won't log correctly
    console.log(this.state.articlesRead);

    this.handleChange();
  }

  render() {
    return (
      <div className='container'>
        <Publication total={(titles) => {this.increaseReads(titles)}} name='Free Code Camp' api={'https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.freecodecamp.org%2Ffeed%2F'}/>
        <Publication total={() => {this.increaseReads()}} name='Code Burst' api={'https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fcodeburst.io%2Ffeed%2F'}/>
        <Publication total={() => {this.increaseReads()}} name='JavaScript Scene' api={'https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.com%2Ffeed%2Fjavascript-scene%2F'}/>
        <Publication total={() => {this.increaseReads()}} name='Hacker Noon' api={'https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fhackernoon.com%2Ffeed'}/>
      </div>
    )
  }
}

I'm sure it is something small, but any help would be greatly appreciated!

Answer

Anthony N picture Anthony N · Feb 12, 2018

The issue might be that you are expecting this.setState to be synchronous. See the documentation here.

Take a look at this CodeSandbox demo. this.setState accepts a callback as the second argument. This callback is invoked after this.setState has completed.

Notice how in the console.log output, we can see the old and new state values.