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