Як отримати доступ до стану всередині редуктора 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;

Як я можу отримати доступ до цього поля?



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

Відповіді:


105

Для цього я використав би thunk , ось приклад:

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

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

В основному ви отримуєте всі дані, необхідні для дії, а потім можете надсилати ці дані своєму редуктору.


4
Будь-яка ідея, чому поточний стан не піддається редуктору самим REDUX ..? здається дивним, що при відправленні мені потрібно додати нерелевантні речі до редуктора, щоб лише початковий стан не замінював поточний стан інших не пов’язаних речей із поточним диспетчерським викликом
vsync

10

Ваші варіанти - написати більше логіки, крім простого використання combineReducers, або включити більше даних у дію. Поширені запитання щодо Redux охоплюють цю тему:

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

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


Дякую за вашу відповідь. Я вивчив деякі зв’язані вами ресурси і буду продовжувати це робити. Однак запитання для вас, оскільки ви, здається, обізнані. Інша відповідь пропонувала використовувати thunk. Чи вважаєте ви, що це було б гарним рішенням моєї проблеми? Якщо це зіпсує мій код або це не найкраща практика, це не рішення, яке я хочу переслідувати, але я не відчуваю себе достатньо обізнаним, щоб визначити довгострокові наслідки цього вибору.
twilco

3
Так, thunks - це стандартний базовий підхід до управління побічними ефектами та виконання складної логіки, що включає кілька відправок та використання поточного стану програми. Цілком нормально писати функцію thunk, яка читає поточний стан програми та виконує такі дії, як умовна відправка, відправлення декількох дій або захоплення частини стану, і включає це в відправлену дію. У мене є кілька прикладів поширених схем обману на gist.github.com/markerikson/ea4d0a6ce56ee479fe8b356e099f857e .
markerikson

2

Я не впевнений, що такий підхід є анти-шаблоном, але він спрацював для мене. Використовуйте функцію curried у своїх діях.

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

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 має доступ до облікового записуДеталі.

Перевага цього підходу полягає в тому, що ви виставляєте лише фрагменти стану, який вам потрібен, а не весь стан.


0

Я вважаю, що ви передаєте це творцю дій. Тож десь у вас з’явиться творець дій, який робить щось подібне:

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

У місці, де ви ініціюєте дію, припустимо, що ви використовуєте реакцію, ви можете використовувати

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

і підключіться до вашого компонента реакції, використовуючи з'єднання React-Redux.

connect(mapStateToProps)(YourReactComponent);

Тепер у вашому компоненті реакції, де ви запускаєте дію updateProduct, ви повинні мати stateOfResidenceId як підказку, і ви можете передати його своєму творцеві дій.

Це звучить спантеличено, але насправді йдеться про поділ проблем.


0

Ви можете спробувати використовувати:

redux-named-редуктори

Що дозволяє отримати стан у будь-якому місці коду, наприклад:

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

Але спочатку подумайте, чи було б краще передавати зовнішній стан як корисне навантаження в дії.


0

Альтернативний спосіб, якщо ви використовуєте реакцію-редукцію і потребуєте цієї дії лише в одному місці, АБО все гаразд із створенням 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. Отже, іншими словами, ця функція додасть це до компонентного реквізиту (синтаксис TypeScript):

{hydratedUpdateProduct: () => 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 та найкращих допоміжних пакетів. І незважаючи на те, що пакет є набагато універсальнішим, документацію легше зрозуміти, оскільки вона не заблукає в деталях, як це зазвичай робить документація reduxs.


-1

Під час відправлення дії ви можете передати параметр. У цьому випадку ви можете перейти accountDetails.stateOfResidenceIdдо дії, а потім передати її редуктору як корисне навантаження.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.