Li Enze
Li Enze

Reputation: 699

How to fetch data and setState when prop changes in React?

Please watch the following code. I know how to fetch data and render the component with the data after the component mounts. But the component needs to fetch other data to re-render when the prop changes. Before, I can use componentWillReceiveProps() to fulfill this easily, but this method is not recommended now and setState() can't be called in componentDidUpdate(). I don't know how to solve this problem.

class HotList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: true,
      songs: [],
    };
  }

  componentDidMount() {
    this.fetchHotList(this.props.platform);
  }

  // The following method is not recommended for use in current React version.
  // componentWillReceiveProps(nextProps) {
  //   this.fetchHotList(nextProps.platform);
  // }

  // setState() can't be called in componentDidUpdate()
  componentDidUpdate(prevProps) {
    this.fetchHotList(this.props.platform);
  }

  fetchHotList(platform) {
    this.setState({
      loading: true,
    });
    fetch(`/api/hot_list/${platform}`, {
      credentials: 'include',
    }).then(res => res.json())
      .then(json => {
        if (json.status === 'ok') {
          this.setState({
            loading: false,
            songs: json.data.songs,
          });
        }
      })
      .catch(err => console.error(err));
  }

  render() {
    const { songs } = this.state;
    return (
      <div>
        {
          this.state.loading ?
            <Icon type="loading" /> :
            <SongList songs={songs}
            />
        }
      </div>
    );
  }
}

export default HotList;

Upvotes: 1

Views: 1291

Answers (2)

Ryan Nghiem
Ryan Nghiem

Reputation: 2438

You should check preProps when using componentDidUpdate() to avoid infinity loop

componentDidUpdate(prevProps) {
    if (prevProps.platform !== this.props.platform)
        this.fetchHotList(this.props.platform);
  }

I think maybe your this is this of fecth callback function.

Well you can change a little code:

fetchHotList(platform) {
    const that = this;
    that.setState({
      loading: true,
    });
    fetch(`/api/hot_list/${platform}`, {
      credentials: 'include',
    }).then(res => res.json())
      .then(json => {
        if (json.status === 'ok') {
          that.setState({
            loading: false,
            songs: json.data.songs,
          });
        }
      })
      .catch(err => console.error(err));
  }

Upvotes: 1

Ismael Padilla
Ismael Padilla

Reputation: 5566

Not using setState in componentDidUpdate is more of a recommendation than a hard rule. You can do it, but you must be careful or you might end up in a componentDidUpdate-setState loop. As per the docs:

You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop.

As per the docs above, you can do something like this:

componentDidUpdate(prevProps) {
    if (prevProps.platform != this.props.platform)
        this.fetchHotList(this.props.platform);
  }

Upvotes: 3

Related Questions