Redux - управление состоянием предварительной загрузки

Я создаю приложение, где мне нужно предварительно загрузить people и planet данные (вероятно, что в будущем могут быть добавлены дополнительные требования предварительной загрузки) при запуске приложения. Я хочу иметь значение в хранилище, которое представляет глобальное состояние приложения как loaded: <boolean>. Значение будет истинным только тогда, когда требования предварительной загрузки people.loaded: true и planet.loaded: true верны. Магазин будет выглядеть примерно так:

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

Отдельные создатели действия делают необходимые асинхронные запросы и действия отправки, которые обрабатываются редукторами people и Planets. Как показано ниже (использует 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 обрабатывает выборку людей и данных планеты либо из LocalStorage, либо из запроса.

initApp() action creator вызывает других создателей действий в site.js непосредственно перед рендерингом в DOM, как показано ниже:

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. Имеет ли глобальное состояние loaded в хранилище?

3. Что может быть масштабируемым способом проверки состояния приложения loaded в нескольких компонентах React? Неправильно включать people и planet состояние для контейнеров, которые просто должны знать состояние глобального приложения и не обрабатывает рендеринг people или Planets. Также было бы больно управлять, когда глобальное состояние loaded было бы необходимо в нескольких контейнерах.


Цитата из ответа Dan от Redux - несколько магазинов, почему бы и нет? вопрос.

Использование композиции редуктора позволяет легко реализовать "зависимые обновления" a la waitFor в Flux, написав редуктор, вручную вызывающий другие редукторы с дополнительной информацией и в определенном порядке.

4. Позволяет ли Дэн призывать другие редукторы вызывать вложенные редукторы?

Ответы

Ответ 1

Сначала позвольте мне исправить ваш пример.

Вместо

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

вы можете (и должны) писать

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

Вам не нужно передавать dispatch в качестве промежуточного ПО аргумента. Конечно, технически ваш код действителен, но я думаю, что мое предложение читается легче.


  • Каковы наилучшие методы управления глобальным состоянием предварительной загрузки приложения в Redux?

То, что вы делаете, кажется правильным. Нет конкретных рекомендаций.

  1. Требуется наличие глобального загруженного состояния в хранилище?

Нет. Как говорит Дэвид в его ответе, вам лучше сохранить только необходимое состояние.

  1. Что может быть масштабируемым способом проверки состояния загруженного приложения в нескольких компонентах React? Не представляется правильным включать в список People и Planet состояние контейнеров, которые просто должны знать глобальное состояние приложения и не обрабатывать передачу людей или планет. Также было бы больно управлять, когда глобальное загруженное состояние потребуется в нескольких контейнерах.

Если вас беспокоит дублирование, создайте функцию "селектор" и поместите ее рядом с редукторами:

// 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
  };
}
  1. Помогает ли Дэн путем вызова других редукторов вызывать вложенные редукторы?

Да, состав редуктора обычно означает вызов вложенных редукторов.
Пожалуйста, обратитесь к моим бесплатным учебным видеороликам Egghead по этой теме:

Ответ 2

Загруженный флаг в вашем состоянии хранилища имеет смысл.

Однако у вас их слишком много. У вас есть state.people.loaded, state.planets.loaded, а также state.loaded. Последнее происходит от первых двух. Ваш магазин действительно не должен содержать производное состояние. Либо есть только первые два или только последние.

Моя рекомендация состояла бы в том, чтобы сохранить первые два, т.е. state.people.loaded и state.planets.loaded. Тогда ваш подключенный компонент может получить конечное загруженное состояние. например.

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