Как получить доступ к состоянию внутри редуктора Redux?

86

У меня есть редуктор, и для расчета нового состояния мне нужны данные из действия, а также данные из части состояния, не управляемой этим редуктором. В частности, в редукторе, который я покажу ниже, мне нужен доступ к accountDetails.stateOfResidenceIdполю.

initialState.js:

export default {
    accountDetails: {
        stateOfResidenceId: '',
        accountType: '',
        accountNumber: '',
        product: ''
    },
    forms: {
        blueprints: [

        ]
    }
};

formsReducer.js:

import * as types from '../constants/actionTypes';
import objectAssign from 'object-assign';
import initialState from './initialState';
import formsHelper from '../utils/FormsHelper';
export default function formsReducer(state = initialState.forms, action) {
  switch (action.type) {
    case types.UPDATE_PRODUCT: {
        //I NEED accountDetails.stateOfResidenceId HERE
        console.log(state);
        const formBlueprints = formsHelper.getFormsByProductId(action.product.id);
        return objectAssign({}, state, {blueprints: formBlueprints});
    }

    default:
      return state;
  }
}

index.js (корневой редуктор):

import { combineReducers } from 'redux';
import accountDetails from './accountDetailsReducer';
import forms from './formsReducer';

const rootReducer = combineReducers({
    accountDetails,
    forms
});

export default rootReducer;

Как я могу получить доступ к этому полю?

Twilco
источник
8
Это утверждение не имеет смысла. Весь смысл функции редуктора в том, что вы принимаете решения на основе текущего состояния и действия.
markerikson

Ответы:

105

Я бы использовал для этого thunk , вот пример:

export function updateProduct(product) {
  return (dispatch, getState) => {
    const { accountDetails } = getState();

    dispatch({
      type: UPDATE_PRODUCT,
      stateOfResidenceId: accountDetails.stateOfResidenceId,
      product,
    });
  };
}

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

Crysfel
источник
4
Есть идеи, почему текущее состояние не отображается в редукторе самим REDUX ..? кажется странным, что при диспетчеризации мне нужно добавить нерелевантные вещи в редуктор, чтобы начальное состояние не переопределяло текущее состояние других несвязанных вещей с текущим вызовом диспетчеризации
vsync
10

Вы можете либо написать больше логики помимо простого использования combineReducers, либо включить больше данных в действие. Redux FAQ охватывает эту тему:

https://redux.js.org/faq/reducers/

Кроме того, в настоящее время я работаю над новым набором страниц документации Redux по теме «Структурирование редукторов», которые могут быть вам полезны. Текущие страницы WIP находятся по адресу https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StructuringReducers.md .

маркериксон
источник
Спасибо за Ваш ответ. Я изучил некоторые ресурсы, на которые вы указали ссылку, и буду продолжать это делать. Однако вопрос к вам, так как вы, кажется, хорошо осведомлены. Другой ответ предполагал использование thunk. Как вы думаете, это будет хорошим решением моей проблемы? Если это приведет к беспорядку в моем коде или это не лучшая практика, это не то решение, которое я хочу искать, но я не чувствую себя достаточно осведомленным, чтобы определить долгосрочные последствия этого выбора.
twilco 01
3
Да, преобразователи - это стандартный базовый подход к управлению побочными эффектами и выполнению сложной логики, которая включает в себя несколько отправок и использование текущего состояния приложения. Совершенно нормально написать функцию преобразователя, которая считывает текущее состояние приложения и выполняет такие действия, как условная отправка, отправка нескольких действий или захват части состояния и включение ее в отправляемое действие. У меня есть несколько примеров распространенных шаблонов thunk на gist.github.com/markerikson/ea4d0a6ce56ee479fe8b356e099f857e .
markerikson
2

Я не уверен, что этот подход является антипаттерном, но у меня он сработал. Используйте в своих действиях каррированную функцию.

export const myAction = (actionData) => (dispatch, getState) => {
   dispatch({
      type: 'SOME_ACTION_TYPE',
      data: actionData,
      state: getState()
   });
}
пользователь3377090
источник
1

Написать свою собственную функцию комбинирования, которая будет делать именно то, что вы хотите, просто:

import accountDetails from './accountDetailsReducer';
import forms from './formsReducer';

const rootReducer = (state, action) => {
        const newState = {};

        newState.accountDetails = accountDetails(state.accountDetails, action);
        newState.forms = forms(state.forms, action, state.accountDetails);

        return newState;
    };

export default rootReducer; 

Тогда ваш FormReducer будет:

export default function formsReducer(state = initialState.forms, action, accountDetails) {

FormReducer теперь имеет доступ к accountDetails.

Преимущество этого подхода в том, что вы предоставляете только нужные вам фрагменты состояния, а не все состояние.

GhostEcho
источник
0

Я предлагаю вам передать его в создатель действий. Итак, где-нибудь у вас будет создатель действий, который делает что-то вроде этого:

updateProduct(arg1, arg2, stateOfResidenceId) {
  return {
    type: UPDATE_PRODUCT,
    stateOfResidenceId
  }
}

Предположим, вы используете реакцию в том месте, где вы запускаете действие, вы можете использовать

function mapStateToProps(state, ownProps) {
  return {
    stateOfResidenceId: state.accountdetails.stateOfResidenceId
  }  
}

и подключитесь к вашему компоненту реакции с помощью connect-redux.

connect(mapStateToProps)(YourReactComponent);

Теперь в вашем компоненте реакции, где вы запускаете действие updateProduct, вы должны иметь stateOfResidenceId в качестве опоры, и вы можете передать его своему создателю действия.

Это звучит запутанно, но на самом деле речь идет о разделении интересов.

Син Ван
источник
0

Вы можете попробовать использовать:

redux-named-redurs

Это позволяет вам получить состояние в любом месте вашего кода следующим образом:

const localState1 = getState(reducerA.state1)
const localState2 = getState(reducerB.state2)

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

miles_christian
источник
0

Альтернативный способ, если вы используете response-redux и нуждаетесь в этом действии только в одном месте ИЛИ все в порядке с созданием HOC (компонент более высокого уровня, на самом деле не нужно понимать, что важно то, что это может раздувать ваш html) везде, где вам нужно этот доступ заключается в использовании mergeprops с дополнительными параметрами, передаваемыми в действие:

const mapState = ({accountDetails: {stateOfResidenceId}}) => stateOfResidenceId;

const mapDispatch = (dispatch) => ({
  pureUpdateProduct: (stateOfResidenceId) => dispatch({ type: types.UPDATE_PRODUCT, payload: stateOfResidenceId })
});

const mergeProps = (stateOfResidenceId, { pureUpdateProduct}) => ({hydratedUpdateProduct: () => pureUpdateProduct(stateOfResidenceId )});

const addHydratedUpdateProduct = connect(mapState, mapDispatch, mergeProps)

export default addHydratedUpdateProduct(ReactComponent);

export const OtherHydratedComponent = addHydratedUpdateProduct(OtherComponent)

Когда вы используете mergeProps, то, что вы возвращаете, будет добавлено к реквизитам, mapState и mapDispatch будут служить только для предоставления аргументов для mergeProps. Другими словами, эта функция добавит это в свойства вашего компонента (синтаксис машинописного текста):

{hydratedUpdateProduct: () => void}

(обратите внимание, что функция фактически возвращает само действие, а не void, но в большинстве случаев вы проигнорируете это).

Но вы можете:

const mapState = ({ accountDetails }) => accountDetails;

const mapDispatch = (dispatch) => ({
  pureUpdateProduct: (stateOfResidenceId) => dispatch({ type: types.UPDATE_PRODUCT, payload: stateOfResidenceId })
  otherAction: (param) => dispatch(otherAction(param))
});

const mergeProps = ({ stateOfResidenceId, ...passAlong }, { pureUpdateProduct, ... otherActions}) => ({
  ...passAlong,
  ...otherActions,
  hydratedUpdateProduct: () => pureUpdateProduct(stateOfResidenceId ),
});

const reduxPropsIncludingHydratedAction= connect(mapState, mapDispatch, mergeProps)

export default reduxPropsIncludingHydratedAction(ReactComponent);

это предоставит реквизитам следующее:

{
  hydratedUpdateProduct: () => void,
  otherAction: (param) => void,
  accountType: string,
  accountNumber: string,
  product: string,
}

В целом, хотя полное несогласие разработчиков redux с расширением функциональности своего пакета для правильного включения таких пожеланий, которые могли бы создать шаблон для этих функций БЕЗ поддержки фрагментации экосистемы, впечатляет.

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

Sam96
источник
-1

При отправке действия вы можете передать параметр. В этом случае вы можете перейти accountDetails.stateOfResidenceIdк действию, а затем передать его редуктору в качестве полезной нагрузки.

Дэви
источник