什么时候ReactJS组件应该使AJAX调用从props更新状态?

时间:2015-06-08 08:40:26

标签: javascript ajax reactjs

我有一个React组件,显示有关实体的信息。实体的id通过属性传入。该组件在" componentDidMount"中启动AJAX调用。在调用完成/失败时获取实体并更新状态。

这种方法很好,只是当实体ID发生变化时,组件不会获取新数据(通过道具)。

我已尝试在" componentWillReceiveProps"但在那个阶段,组件仍然具有旧属性集。我必须将nextProps传递给AJAX调用方法,这看起来并不正确。

让组件异步更新其状态以响应属性更改的最佳/最干净方法是什么?

2 个答案:

答案 0 :(得分:9)

我也是新手,所以Flux架构对我来说有点吓人。我正如你所说的那样,使用componentWillMount通过AJAX加载初始数据,然后使用componentWillReceiveProps加载nextProps,如果/当道具发生变化时再次加载新数据:

var Table = React.createClass({
  getInitialState: function() {
    return { data: [] };
  },

  componentWillMount: function(){
    this.dataSource();
  },

  componentWillReceiveProps: function(nextProps){
    this.dataSource(nextProps);
  },

  dataSource: function(props){
    props = props || this.props;

    return $.ajax({
      type: "get",
      dataType: 'json',
      url: '/products?page=' + props.page + "&pageSize=" + props.pageSize
    }).done(function(result){
      this.setState({ data: result });
    }.bind(this));
  },

  render: function() {
    return (
      <table className="table table-striped table-bordered">
        <Head />
        <Body data={this.state.data}/>
      </table>
    );
  }
});

答案 1 :(得分:0)

钩子componentWillMountcomponentWillReceiveProps自从React v16.3.0(source)开始被弃用。

当您需要在首次呈现组件(source)之后立即加载数据时,应该在componentDidMount钩子处进行

AJAX请求。如果您希望在更改一些道具后刷新数据,则必须使用componentDidUpdate挂钩。

但是您还必须处理另外三个生命周期挂钩,以避免启动请求/更新的无限循环。假设您希望基于props.category的更改来更新帖子列表:

  • state应该具有两个属性,categorycurrentCategory,在组件的构造函数上设置null;
  • getDerivedStateFromProps需要从新的state.category更新props.category
  • shouldComponentUpdate需要比较state.categorystate.currentCategory,以确定是否应更新组件;
  • getSnapshotBeforeUpdate是确定componentDidUpdate是否应发出AJAX请求或更改state.currentCategory值并完成更新周期的必要条件。

完整的代码如下(source):

import React, { Component, Fragment } from 'react';
import axios from "axios";

class Post extends Component {

 constructor(props) {
    super(props);

    this.state = {
      posts: [],
      category: null,
      currentCategory: null
    };
   
   this._createMarkup = this._createMarkup.bind();
 }

  static getDerivedStateFromProps(props, state) {
    if (props.category !== state.category) {
      return {
        category: props.category
      };
    }
    return null;
  }

  componentDidMount() {
    this._fetchData();
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.state.currentCategory !== nextState.category;
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    return prevState.currentCategory !== prevState.category;
  }

  componentDidUpdate(prevProps, prevState, dataDidFetch) {
   // dataDidFetch is returned by getSnapshotBeforeUpdate
    if (dataDidFetch) {
      this.setState({
        currentCategory: this.state.category
      });
    } else {
      this._fetchData();
    }
  }

  _fetchData() {
    const category = this.state.category; 
    axios.get(`/some/api/endpoint?category=${category}`).then(posts => {
      this.setState({
        posts: posts.data
      });
    });
  }

  _createMarkup(html) {
    return { __html: html };
  }

  render() {
    return (
      <Fragment>
        {this.state.posts.map(post => (
            <article className="post" key={post.id}>
              <h2>{post.title.rendered}</h2>
              <div dangerouslySetInnerHTML={this._createMarkup( post.content.rendered )} />
              <p className="post-link">
                <a href="{post.resource_link_url}">{post.resource_link_label}</a>
              </p>
            </article>        
          ))}
        </Fragment>
      );
  }
}

export default Post;