Как связать действия redux, используя возвращенный результат предыдущего действия?

Я создаю приложение на React Native и использую Redux с redux-persist, чтобы действовать как в базе данных устройства.

Суть проблемы заключается в том, как мне вернуть результат действия redux, чтобы затем отправить другое действие с этими данными? Подробнее читайте в этой статье.

Пользователь может создавать собственные типы привычек. При этом я отправляю действие для создания типа привычки в магазине (например, бег). Это действие создает новый уникальный UUID для типа привычки. Затем я хочу добавить этот вновь созданный тип привычки в рутину (например, утреннюю рутину), и поэтому мне нужно получить обратно UUID типа привычки и вызвать другую отправку, чтобы добавить ее в рутину.

Я использую immer, чтобы упростить управление состоянием в моих редукторах, и имею этот код (упрощенный пример ):

import produce from "immer";

const userReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_CUSTOM_HABIT_TYPE: {
      return produce(state, draftState => {
        const newHabitType = {
          id: generateUuid(),
          name,
        };

        draftState.customHabitTypes.push(newHabitType);

        return draftState;
      });
    }
  }
};

Затем я отправляю его в свой компонент, например (упрощенно):

dispatch({
  type: ADD_CUSTOM_HABIT_TYPE,
  name: "running",
});

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

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


person fredrivett    schedule 02.07.2020    source источник


Ответы (2)


Очень простым решением было бы сгенерировать уникальный идентификатор перед отправкой действия.

Пример

const newHabitType = {
  id: generateUuid(),
  name,
};

dispatch({
  type: ADD_CUSTOM_HABIT_TYPE,
  habit: newHabitType,
});

dispatch({
  type: ADD_CUSTOM_HABIT_TO_ROUTINE,
  habit: newHabitType.id,
});

Плюсы

  1. Вам больше не нужно связывать действия как таковые, вам просто нужно распределить их по порядку.
  2. Это сохраняет одно из самых важных правил Redux: ваш редуктор не должен иметь никаких побочных эффектов (в вашем случае, генерации случайного идентификатора). ссылка

Минусы

  1. Если вы создаете новые привычки в нескольких местах, вам придется генерировать уникальные идентификаторы в каждом месте, где вы отправляете действие. Это может привести к повторению кода. Решением этой проблемы было бы инкапсулировать всю логику создания привычек в одном компоненте, а затем повторно использовать этот компонент повсюду.
person Khaled Sakr    schedule 03.07.2020
comment
Привет, Халед, спасибо за ответ! Я думаю, что вы правы, я сам обдумывал это, но затем всплыл упомянутый вами Con, поэтому я почувствовал, что должен быть сокращенный способ делать что-то, чтобы позволить действию ADD_CUSTOM_HABIT_TYPE полностью инкапсулировать создание этого нового типа привычки (включая UUID поколение). Из документов, на которые вы ссылаетесь, кажется, что это не способ сокращения. Я посмотрю, как я могу инкапсулировать логику в функцию или компонент, в зависимости от того, что подходит лучше всего. Спасибо за разъяснение. - person fredrivett; 03.07.2020
comment
Просто обновление здесь, используя redux-thunk для создания функции, которая генерирует UUID и передает его обеим диспетчерам (в основном точный код, который Халед привел выше, только что завернутый в преобразователь), отлично справляется со своей задачей и позволяет мне вызывать dispatch(ADD_CUSTOM_HABIT_TYPE_THUNK({ habit }) с помощью thunk, обрабатывающий генерацию UUID и оба вызова действия redux. - person fredrivett; 04.07.2020

Действия не возвращают данные как таковые, это просто объекты, которые изменяют хранилище на основе правил, определенных в редукторе. Два возможных решения:

Вариант А: создание составного действия.

const compositeAction = args => {
    return dispatch => {
        return someAsyncCall(args).then(response => {
           dispatch(addCustomHabitat(response))
           dispatch(followUpAction())
        }
    }
}

const addCustomHabitat = response => {
    return {
        type: "ADD_CUSTOM_HABIT_TYPE",
        data: response
    }
}

const followUpAction = () => {
    ...another action...
}

Вариант Б: подключите результаты первого действия к компоненту диспетчеризации через react-redux и передайте их второму действию.

import {connect} from 'react-redux';

const MyReactComponent = props => {
    dispatch(addCustomHabitatTypeAction());

    if(props.customHabitatType !== undefined)
        dispatch(followUpAction(props.customHabitatType());

    return (
       ...JSX here...
    );
}

const mapStateToProps = state => {
    return {
        customHabitatType: state.userReducer.customHabitatType
    }
}

connect(mapStateToProps)(MyReactComponent);

Надеюсь, это поможет! Прошу прощения за мой сокращенный код и дайте мне знать, если у вас возникнут вопросы.

person Daly    schedule 02.07.2020
comment
Спасибо за ответ, Дейли! Позвольте мне попытаться понять вещи. В Option A, я думаю, вы предлагаете убрать создание UUID из действия, поместить его в новую функцию someAsyncCall, а затем передать этот новый UUID как действиям ADD_CUSTOM_HABIT_TYPE, так и ADD_HABIT_TYPE_TO_ROUTINE? В Option B я менее ясен. Как этот компонент узнает, что изменилось? Поскольку может быть 5 существующих пользовательских типов привычек, после изменения их будет 6, нужно ли мне вручную изменять различия, чтобы получить новый? Не уверен, как это должно работать. Спасибо! - person fredrivett; 03.07.2020
comment
Для варианта A, если генерация UUID должна выполняться асинхронно, то да, вы можете использовать этот подход. Для варианта B компонент повторно отображается каждый раз, когда действие изменяет значение cusomHabitatType. Затем вы можете использовать условные выражения внутри компонента, чтобы принимать дальнейшие решения на основе значения этого свойства. В целом, честно говоря, я думаю, что решение Халеда лучше послужит вам, если логика будет простой. :) Это немного более тяжелые варианты. - person Daly; 04.07.2020
comment
Спасибо, Дейли, ценим мысли и предложения здесь! Поняв, что метод redux не должен иметь побочных эффектов, я думаю, что я постараюсь реорганизовать создание UUID, аналогично решению Халеда. - person fredrivett; 04.07.2020