如何访问Redux reducer内部的状态

How to access state inside Redux reducer?

本文关键字:reducer 内部 状态 Redux 何访问 访问      更新时间:2023-09-26

我有一个减速器,为了计算新状态,我需要来自动作的数据以及来自该减速器未管理的状态的一部分的数据。具体来说,在下面的reducer中,我需要访问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 (root reducer):

import { combineReducers } from 'redux';
import accountDetails from './accountDetailsReducer';
import forms from './formsReducer';
const rootReducer = combineReducers({
    accountDetails,
    forms
});
export default rootReducer;

如何访问该字段?

我会用think来表示,这里有一个例子:

export function updateProduct(product) {
  return (dispatch, getState) => {
    const { accountDetails } = getState();
    dispatch({
      type: UPDATE_PRODUCT,
      stateOfResidenceId: accountDetails.stateOfResidenceId,
      product,
    });
  };
}

基本上你得到了所有你需要的关于动作的数据,然后你可以把这些数据发送给你的reducer

除了使用combineReducers之外,您的选择是编写更多逻辑,或者在操作中包含更多数据。Redux FAQ涵盖了这个主题:

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

另外,我目前正在为Redux文档创建一组新的页面,主题是"Structuring Reducers",这可能会对您有所帮助。当前WIP页面在https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StructuringReducers.md .

我不确定这种方法是否是反模式,但它对我有效。在操作中使用柯里化函数

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

编写自己的组合函数是很简单的,它可以做你想做的事情:

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。

这种方法的好处是您只公开了需要的状态片,而不是整个状态。

我建议您将它传递给操作创建器。在某个地方你会有一个动作创建器它会做这样的事情:

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

在你触发动作的地方,假设你使用的是react,你可以使用

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

并使用react-redux的connect连接到你的react组件。

connect(mapStateToProps)(YourReactComponent);

现在,在你触发动作updateProduct的react组件中,你应该有statofresidenceid作为一个道具,你可以把它传递给你的动作创建者。

这听起来很复杂,但它确实是关于关注点分离的。

您可以尝试使用:

redux-named-reducers

允许您在代码中的任何地方获取状态,如下所示:

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

但是首先考虑是否将外部状态作为动作中的有效负载传递会更好。

另一种方法是,如果您使用react-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时,你返回的东西将被添加到props中,mapState和mapDispatch将只用于为mergeProps提供参数。所以,换句话说,这个函数会把这个添加到你的组件props (typescript语法):

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

this将为props提供以下内容:

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

总的来说,尽管redux维护者完全不赞成以一种好的方式扩展他们的包的功能来包含这些愿望,这将为这些功能创建一个模式,而不支持生态系统的碎片,这是令人印象深刻的。

像Vuex这样不那么顽固的包不会有那么多滥用反模式的问题,因为它们会丢失,同时支持一种比redux和最好的支持包更干净的语法和更少的样板文件。尽管软件包更加通用,但文档更容易理解,因为它们不会像reduxs文档那样迷失在细节中。

如果您可以访问store,那么您应该使用store.getState()

https://redux.js.org/api/store getstate

调度操作时,可以传递一个参数。在这种情况下,您可以将accountDetails.stateOfResidenceId传递给动作,然后将其作为有效负载传递给reducer。