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

У меня есть редуктор, и для вычисления нового состояния мне нужны данные из действия, а также данные из части состояния, которой не управляет этот редуктор. В частности, в редукторе, который я покажу ниже, мне нужен доступ к полю 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;

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

Ответ 1

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

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

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

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

Ответ 2

Ваши варианты - либо написать больше логики, кроме как использовать combineReducers, либо включить в действие больше данных. В FAQ Redux входит эта тема: http://redux.js.org/docs/faq/Reducers.html#reducers-share-state.

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

Ответ 3

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

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

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

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

и подключитесь к вашему реагирующему компоненту, используя соединение "реакция-редукция".

connect(mapStateToProps)(YourReactComponent);

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

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

Ответ 4

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

Redux название-редукторы

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

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

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

Ответ 5

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

export const myAction = (actionData) => (dispatch, getState) => {
   dispatch({
      type: 'SOME_ACTION_TYPE',
      data: actionData,
      state: getState()
   });
}

Ответ 6

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

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) {

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

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

Ответ 7

Альтернативный способ, если вы используете response-redux и вам нужно, чтобы это действие выполнялось только в одном месте, ИЛИ все в порядке с созданием HOC (более высокого компонента oder), вам не нужно понимать, что важно то, что это может раздуть ваш 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, то, что вы там возвращаете, будет добавлено в props, 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,
}

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

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

Ответ 8

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