Redux - 管理预加载状态

16

我正在构建一个应用程序,在启动应用程序时需要预加载peopleplanet数据(未来可能会增加更多的预加载需求)。我希望有一个值在存储中表示应用程序的全局状态为loaded: <boolean>。只有当预加载要求people.loaded: trueplanet.loaded: true都为true时,该值才为真。存储将如下所示:

Store
├── loaded: <Boolean>
├── people:
│   ├── loaded: <Boolean>
│   └── items: []
├── planets:
│   ├── loaded: <Boolean>
│   └── items: []

分离的 action creator 进行所需的异步请求,并分发由 `People` 和 `Planets` reducer 处理的 actions。如下所示(使用 redux-thunk):

actions/index.js

import * as types from '../constants/action-types';
import {getPeople, getPlanets} from '../util/swapi';

export function loadPeople () {
  return (dispatch) => {
    return getPeople()
      .then((people) => dispatch(addPeople(people)));
  };
}

export function loadPlanets () {
  return (dispatch) => {
    return getPlanets()
      .then((planets) => dispatch(addPeople(planets)));
  };
}

export function addPeople (people) {
  return {type: types.ADD_PEOPLE, people};
}

export function addPlanets (planets) {
  return {type: types.ADD_PLANETS, planets};
}

export function initApp () {
  return (dispatch) => {
    loadPeople()(dispatch);
    loadPlanets()(dispatch);
  };
}

../util/swapi 处理从本地存储获取人物和星球数据,或者发起请求。

initApp() 动作创建器在呈现到DOM之前调用 site.js 中的其他动作创建器,如下所示:

site.js

import React from 'react';
import {render} from 'react-dom';
import Root from './containers/root';
import configureStore from './store/configure-store';
import {initApp} from './actions';

const store = configureStore();

// preload data
store.dispatch(initApp());

render(
  <Root store={store} />,
  document.querySelector('#root')
);

1. Redux中管理全局预加载状态的最佳实践是什么?

2. 在store中拥有全局的loaded状态是必要的吗?

3. 检查多个React组件中应用程序的loaded状态的可扩展方法是什么? 在那些仅需要知道全局应用状态而不处理PeoplePlanets渲染的容器中包含这些状态似乎并不正确。而且当全局loaded状态在多个容器中需要时,管理起来也会很痛苦。


引用Dan在Redux - multiple stores, why not?问题中的回答部分。

使用reducer组合技术可以通过编写一个手动调用其他reducer的reducer,并按特定顺序提供其他信息来轻松实现“依赖更新”,类似于Flux中的waitFor。

4. Dan通过调用其他reducer指的是调用嵌套的reducer吗?

2个回答

17
首先,让我更正您的例子。
改为:

export function initApp () {
  return (dispatch) => {
    loadPeople()(dispatch);
    loadPlanets()(dispatch);
  };
}

你可以(而且应该)编写

export function initApp () {
  return (dispatch) => {
    dispatch(loadPeople());
    dispatch(loadPlanets());
  };
}

您不需要将dispatch作为参数传递——thunk中间件会处理这个问题。
当然,从技术上讲,您的代码是有效的,但我认为我的建议更容易阅读。


  1. 管理Redux应用程序的全局预加载状态的最佳实践是什么?

您正在进行的操作似乎是正确的。没有特定的最佳实践。

  1. 在存储库中拥有全局加载状态是否必要?

不需要。正如David在他的答案中所指出的那样,最好只存储必要的状态。

  1. 检查多个React组件中应用程序加载状态的可扩展方法是什么?将People和Planet状态包含在只需要知道全局应用程序状态并且不处理People或Planets渲染的容器中似乎不正确。而且,当多个容器需要全局加载状态时,这将是痛苦的。

如果您担心重复,请创建一个“选择器”函数,并将其放在您的reducer旁边:

// Reducer is the default export
export default combineReducers({
  planets,
  people
});

// Selectors are named exports
export function isAllLoaded(state) {
  return state.people.loaded && state.planets.loaded;
}

现在您可以从组件中导入选择器,并在任何组件内的mapStateToProps函数中使用它们:

import { isAllLoaded } from '../reducers';

function mapStateToProps(state) {
  return {
    loaded: isAllLoaded(state),
    people: state.people.items,
    planets: state.planet.items
  };
}
  • 调用其他reducer是否意味着调用嵌套的reducer?
  • 是的,reducer组合通常意味着调用嵌套的reducer。 请参考我关于此主题的免费Egghead教程视频:

    感谢您提供如此全面的答案! - Renārs Vilnis

    6

    您的存储状态中的加载标志是很有意义的。

    但是您有太多这样的标志了。您有state.people.loadedstate.planets.loaded以及state.loaded。后者是从前两个派生出来的。您的存储不应该包含派生状态。只使用前两个或最后一个。

    我的建议是保留前两个,即state.people.loadedstate.planets.loaded。然后,您的连接组件可以派生出一个终极加载状态。例如:

    function mapStateToProps(state) {
        return {
            loaded: state.people.loaded && state.planets.loaded,
            people: state.people.items,
            planets: state.planet.items
        };
    }
    

    但是,如果在应用程序中多次使用state.people.loaded && state.planets.loaded,那么维护它不会很困难吗?Redux示例通常显示这样可以减少可派生状态。但是,我可以将加载检查包装到一个接收状态并返回所需值的函数中。这将更具声明性。 - Renārs Vilnis
    1
    你可能需要在应用程序中多次派生相同的内容,这是完全可能的。这是Redux没有提供解决方案的事情 - 它并不旨在解决这个问题。我仍然会保持存储状态的最小化。我最近采用的一种做法是将重复的功能导出到“utils/”文件夹中。 - David L. Walsh
    你对一个这样的想法有什么看法:在获取请求后,动作创建器将分别向“loader”和“people”或“planet”减速器发送多个操作。这会引起问题,因为分派函数的顺序很重要,比如你不能声明对于“loader”减速器,先于向“People”减速器分派操作之前,必须加载“people”。 - Renārs Vilnis
    1
    @RenārsVilnis 请查看 Redux 存储库中的“购物车”示例。它展示了如何将这种“状态计算”封装到我们称为“选择器”的函数中。将选择器与 reducer 放在一起使用——例如,isAllLoaded(state) 将计算 state.people.loaded && state.planets.loaded 并可在整个应用程序中使用。 - Dan Abramov

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接