React, Redux: как не загружать данные в хранилище дважды

Установка

У меня есть приложение React / Redux, которое загружает список cats из API.

Данные загружаются в компонент следующим образом:

// 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, когда этот другой маршрут монтируется, если оба сначала проверяют существующие данные хранилища?

Если проверка полезна, есть ли лучший способ сделать это?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
0
1 769
3

Ответы 3

Да, я бы хотел, чтобы ваши действия redux выглядели примерно так: GET_CATS, GET_CATS_SUCCESS и GET_CATS_ERROR.

GET_CATS установит состояние loading в хранилище redux на true, таким образом, вы можете опросить его в соответствующих функциях componentDidMount() и делать вызов API только тогда, когда loading ложный. Я думаю, что это довольно распространенный способ сделать это.

По сути, у меня это есть, хотя loaded: false установлен в initialState редуктора, а не в отдельном действии GET_CATS.

crowhill 29.03.2018 22:28

@crowhill Вы захотите отслеживать больше, чем просто loaded. Вы захотите узнать, был ли сделан запрос. Так что loading более точен. Вы узнаете, загружены ли данные, просто посмотрев, есть ли они там. Таким образом, ключ loaded, вероятно, избыточен

Max Millington 30.03.2018 00:06

Если я правильно понимаю ваш вопрос, вы хотите видеть, загружены ли данные для отдельного класса. Если да, то не вызывайте API для повторной загрузки кошек.

Есть два способа сделать это, предположим, что COM1 и COM2 являются вашими компонентами.

  1. верните весь state, а не только конкретные переменные, которые вы хотите использовать для обоих ваших компонентов:

    состояние возврата

затем укажите кошек в каждом компоненте:

this.props.COM1.cats.items
this.props.COM2.cats.items
  1. вернуть конкретную переменную cats из других компонентов. вы делаете следующее для каждого компонента:

function mapStateToProps(state, ownProps) {

let cats = state.COM1.cats.items;

let loaded: state.cats.loaded;

let otherCats = state.COM2.cats.items;

return {

cats,

otherCats,

loaded

}

}

Все зависит от того, как вы обрабатываете асинхронную выборку данных в 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(), поэтому довольно часто проверять, доступны ли уже данные. Теперь этот подход включает в себя некоторый шаблон, и в конечном итоге он может оказаться утомительным (вам потребуется написать еще три функции: loadRoomsPending, loadRoomsSuccess, loadRoomsError). Таким образом, вашим компонентам не придется проверять это вручную. Или, если вам нравится более явный или чистый, вы можете попробовать промежуточное программное обеспечение, которое я реализовал, я был немного разочарован всем этим шаблоном, поэтому с помощью 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. Вы можете найти промежуточное ПО здесь. Но во что бы то ни стало, просто используйте то, что, по вашему мнению, лучше всего подходит для вашего варианта использования. Мне захотелось поделиться этой работой, потому что это разочарование заставило меня создать промежуточное программное обеспечение для ее обработки. Имейте в виду, что я сделал некоторые предположения по вашему делу, поэтому, если я полностью ошибаюсь, дайте мне знать.

Спасибо за подробный ответ. Что касается варианта B (проверить преобразователь), как бы вы поступили с случаем «данные уже загружены»? Как в getState().cats.length > 0

crowhill 02.04.2018 18:29

Ну, это зависит от вашего логического потока, так что это действительно зависит от вас. Вы можете основывать его на длине, если это массив или что-то еще, что имеет смысл для вашего варианта использования. Проблема в том, что вы хотите действовать, только если данных нет, поэтому я проверил, нет ли данных. Иначе, если вы сделаете if (getState().cats.length > 0), то что именно вы будете делать? Совсем ничего ?

G4bri3l 02.04.2018 22:47

Другие вопросы по теме