Подтвердить что ты не робот

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

У нас есть действие, которое извлекает объект async, назовем его getPostDetails, который принимает параметр, из которого post для извлечения id. Пользователь получает список сообщений и может нажать на один, чтобы получить некоторые сведения.

Если пользователь нажимает "Сообщение № 1", мы отправляем действие GET_POST, которое может выглядеть примерно так.

const getPostDetails = (id) => ({
  type: c.GET_POST_DETAILS,
  promise: (http) => http.get(`http://example.com/posts/#${id}`),
  returnKey: 'facebookData'
})

Это подхватывается промежуточным программным обеспечением, которое добавляет обработчик успеха к обещанию, которое вызовет действие, подобное GET_POST__OK с десериализованным объектом JSON. Редуктор видит этот объект и применяет его к магазину. Типичный __OK редуктор выглядит следующим образом.

[c.GET_ALL__OK]: (state, response) => assign(state, {
  currentPost: response.postDetails
})

Позже вниз по линии у нас есть компонент, который смотрит на currentPost и отображает детали для текущего сообщения.

Однако у нас есть условие гонки. Если пользователь отправляет два действия GET_POST_DETAILS один за другим, есть нет гарантии того, в каком порядке мы получаем действия __OK, если второй HTTP-запрос заканчивается до первого, состояние станет неправильным.

    Action                   => Result
    ---------------------------------------------------------------------------------
|T| User Clicks Post #1      => GET_POST for #1 dispatched => Http Request #1 pending
|i| User Clicks Post #2      => GET_POST for #2 dispatched => Http Request #2 pending
|m| Http Request #2 Resolves => Results for #2 added to state
|e| Http Request #1 Resolves => Results for #1 added to state
 V

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

4b9b3361

Ответ 1

Проблема связана с субоптимальной организацией состояний.

В приложении Redux ключи состояния, такие как currentPost, обычно являются анти-шаблонами. Если вам нужно "reset" состояние при каждом переходе на другую страницу, вы потеряли один из основных преимуществ Redux (или Flux): кеширование. Например, вы не можете перемещаться назад сразу, если какая-либо навигация сбрасывает состояние и повторяет данные.

Лучшим способом хранения этой информации было бы разделение postsById и currentPostId:

{
  currentPostId: 1,
  postsById: {
    1: { ... },
    2: { ... },
    3: { ... }
  }
}

Теперь вы можете загружать столько сообщений одновременно, сколько хотите, и самостоятельно объединять их в кеш postsById, не беспокоясь о том, является ли выбранная запись текущей.

Внутри вашего компонента вы всегда будете читать state.postsById[state.currentPostId] или лучше, экспортировать селектор getCurrentPost(state) из файла редуктора, чтобы компонент не зависел от конкретной формы состояния.

Теперь нет условий гонки, и у вас есть кеш сообщений, поэтому вам не нужно возвращаться, когда вы возвращаетесь. Позже, если вы хотите, чтобы текущая почта контролировалась из строки URL, вы можете полностью удалить currentPostId из состояния Redux и вместо этого прочитать ее с вашего маршрутизатора - остальная часть логики останется прежней.


Пока это не то же самое, у меня случается еще один пример с аналогичной проблемой. Ознакомьтесь с кодом до и после. Это не совсем то же самое, что и ваш вопрос, но, надеюсь, он показывает, как государственная организация может помочь избежать условий гонки и непоследовательных реквизитов.

Я также записал бесплатную серию видео, которая объясняет эти темы, чтобы вы могли проверить.

Ответ 2

Решение Dan, вероятно, лучше, но альтернативным решением является прервать первый запрос, когда начинается второй.

Вы можете сделать это, разделив создателя действия на асинхронный, который может считывать из хранилища и отправлять другие действия, которые позволяет вам делать decx-thunk.

Первое, что должен сделать ваш создатель действия async, это проверить хранилище на существующее обещание и прервать его, если он есть. Если нет, он может выполнить запрос и отправить действие "начало запроса", которое содержит объект обещания, который сохраняется в следующий раз.

Таким образом, будет разрешено только последнее созданное обещание. Когда вы это делаете, вы можете отправить успешное действие с полученными данными. Вы также можете отправить сообщение об ошибке, если по какой-либо причине обещание отклонено.