React,Redux:如何避免将数据加载到商店两次

时间:2018-03-29 19:52:37

标签: reactjs redux react-redux

设置

我有一个React / Redux应用程序,它从API中加载cats列表。

数据被加载到一个组件中,如下所示:

// thunk, etc omitted for clarity.
componentDidMount() {
    if(!this.props.loaded){
        this.props.actions.loadRooms();
    }
}

从这里抽取道具:

function mapStateToProps(state, ownProps) {
    return {
        cats: state.cats.items,
        loaded: state.cats.loaded
    }
}

假设如下:

1)cats将在另一个完全独立的组件中需要,该组件不是当前组件的子组件。

2)我无法知道哪个cats需要的组件将首先被安装。

实际问题

if(!this.props.loaded)有用吗?换句话说,如果两个路由都先挂载,如果两个路由都先检查现有的商店数据,它是否可以节省我对API的理论调用?

如果检查有用,有没有更好的方法呢?

3 个答案:

答案 0 :(得分:3)

是的,我会将您的redux操作看起来像:GET_CATSGET_CATS_SUCCESSGET_CATS_ERROR

GET_CATS会将redux存储中的loading状态设置为true,这样您就可以在相应的componentDidMount()函数中查询它,并且只调用当loading为假时api。我认为这是一种相当常见的方式。

答案 1 :(得分:0)

如果我正确理解了您的问题,您希望能够查看是否还有一个单独的类加载了它的数据。如果是,那么请不要再调用API来加载猫。

有两种方法可以做到这一点,假设COM1和COM2是您的组件。

  1. 返回整个this.props.COM1.cats.items this.props.COM2.cats.items ,而不只是两个组件所需的特定变量:

    返回状态

  2. 然后引用每个组件中的猫:

    function mapStateToProps(state, ownProps) {
    1. 从其他组件返回特定的cat变量。您为每个组件执行以下操作:
    2. let cats = state.COM1.cats.items; let loaded: state.cats.loaded; let otherCats = state.COM2.cats.items; return { cats, otherCats, loaded } }

        <?php
      
      use Illuminate\Support\Facades\Schema;
      use Illuminate\Database\Schema\Blueprint;
      use Illuminate\Database\Migrations\Migration;
      
      class CreateArticleTagTable extends Migration
      {
      /**
       * Run the migrations.
       *
       * @return void
       */
      public function up()
      {
          Schema::create('article_tag', function (Blueprint $table) {
              $table->increments('id');
              $table->unsignedInteger('article_id');
              $table->unsignedInteger('tag_id');
          });
      
      
              $table->foreign('article_id')->references('id')->on('article');
              $table->foreign('tag_id')->references('id')->on('tag');
      
      
      
      /**
       * Reverse the migrations.
       *
       * @return void
       */
       public function down()
       {
           Schema::dropIfExists('article_tag');
       }
      }
      

答案 2 :(得分:0)

这一切都取决于你如何处理redux中的异步数据提取,如果两个兄弟组件都在监听你可以做的代表cats的状态部分:

// Component A and Component B might have something like this
// they both subscribe to the same portion of the state so, if
// data is already available then you don't need to do fetch it again.
...
componentDidMount() {
  if (this.props.cats.length === 0) {
    this.props.actions.loadRooms();
  }
}
...

如果您使用redux-thunk,则可以在action级别控制此项:

function loadRooms() {
  return (dispatch, getState) => {   
    if (getState().cats.length === 0) {
      dispatch(loadRoomsPending());

       fetchMyData(...args)
         .then((res) => dispatch(loadRoomsSuccess(res))
         .catch((err) => dispatch(loadRoomsError(err));
    }
  }
}


// Component A and Component B
...
componentDidMount() {
  this.props.actions.loadRooms();
}
...

此处您可以使用getState()访问当前状态,因此检查数据是否已经可用是非常常见的。现在这种方法带有一些样板,从长远来看可能会变得乏味(它需要你编写另外三个函数loadRoomsPendingloadRoomsSuccessloadRoomsError)。这样,您的组件就不必手动检查它。或者如果你更喜欢它更明确或更清洁你可以提供一个中间件我实施了一次尝试,我对所有这些样板文件感到沮丧,所以使用redux-slim-async你可以这样做:

function loadRooms() {
  return {
    types: [
      actionTypes.LOAD_ROOMS_PENDING,
      actionTypes.LOAD_ROOMS_SUCCESS,
      actionTypes.LOAD_ROOMS_ERROR,
    ],
    callAPI: fetch(...args).then(res => res.json()),
    shouldCallAPI: (state) => state.cats.length === 0,
  };
}

这可以通过符合FSA的操作处理所有事情,并且非常清楚发生了什么。如果你正确设置它,你可以做得更好:

function loadRooms() {
  return {
    typePrefix: actionTypes.LOAD_ROOMS,
    callAPI: fetch(...args).then(res => res.json()),
    shouldCallAPI: (state) => state.cats.length === 0,
  };
}

这将触发格式${typePrefix}_PENDING${typePrefix}_SUCCESS${typePrefix}_ERROR的待处理,成功和错误请求,您可以找到中间件here。但是无论如何只要使用你觉得最适合你用例的任何东西,我觉得分享这项工作是因为它让我构建了一个中间件来处理它的挫败感。请记住,我对你的案子做了一些假设,所以如果我完全离开,请告诉我。