【问题标题】:How to access state inside Redux reducer?如何访问 Redux reducer 中的状态?
【发布时间】:2023-03-22 01:18:01
【问题描述】:

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

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

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

export default rootReducer;

如何访问此字段?

【问题讨论】:

标签: javascript reactjs redux


【解决方案1】:

我会为此使用thunk,这是一个示例:

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

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

基本上你会得到你在操作中需要的所有数据,然后你可以将这些数据发送到你的reducer。

【讨论】:

  • 知道为什么当前状态没有被 REDUX 本身暴露给 reducer 吗?似乎很奇怪,在调度时我需要向减速器添加不相关的东西,这样初始状态就不会覆盖当前调度调用的其他不相关内容的当前状态
【解决方案2】:

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

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

另外,我目前正在为 Redux 文档编写一组新页面,主题为“结构化 Reducers”,您可能会发现这些页面很有帮助。当前的 WIP 页面位于 https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StructuringReducers.md。

【讨论】:

  • 感谢您的回答。我已经探索了您链接的一些资源,并将继续这样做。但是,问你,因为你似乎知识渊博。另一个答案建议使用thunk。你认为这对我的问题有很好的解决方案吗?如果它会弄乱我的代码或者不是最佳实践,那不是我想要追求的解决方案,但我觉得自己的知识不足以确定这些选择的长期影响。
  • 是的,thunk 是管理副作用和执行涉及多次调度和使用当前应用程序状态的复杂逻辑的标准基本方法。编写一个读取当前应用程序状态的 thunk 函数是完全正常的,并执行诸如有条件分派、分派多个动作或抓取部分状态并将其包含在分派动作中的操作。我在gist.github.com/markerikson/ea4d0a6ce56ee479fe8b356e099f857e 有一些常见的thunk 模式示例。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    编写自己的组合函数很简单,完全符合您的要求:

    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。

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

    【讨论】:

      【解决方案5】:

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

      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 的反应组件中,您应该将 stateOfResidenceId 作为道具,您可以将其传递给您的操作创建者。

      这听起来很复杂,但实际上是关于关注点分离。

      【讨论】:

        【解决方案6】:

        你可以尝试使用:

        redux-named-reducers

        这使您可以像这样在代码中的任何位置获取状态:

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

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

        【讨论】:

          【解决方案7】:

          另一种方法,如果您使用 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 提供参数。因此,换句话说,此函数会将其添加到您的组件道具(打字稿语法):

          {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-maintainers 完全反对扩展他们的包的功能以以一种好的方式包含这些愿望,这将为这些功能创建一个模式而不支持生态系统的碎片化,但令人印象深刻。

          像 Vuex 这样不那么顽固的包几乎不会因为人们滥用反模式而出现很多问题,因为它们会迷失方向,同时支持一种更简洁的语法,并且比使用 redux 和最佳支持包归档的样板更少。尽管这个包更加通用,但文档更容易理解,因为它们不会像 reduxs 文档那样迷失在细节中。

          【讨论】:

            【解决方案8】:

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

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

            【讨论】:

              【解决方案9】:

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

              【讨论】:

                猜你喜欢
                • 2017-12-31
                • 2021-09-15
                • 2017-04-02
                • 2020-05-30
                • 1970-01-01
                • 2023-03-23
                • 2016-06-21
                • 2017-11-27
                • 2023-03-23
                相关资源
                最近更新 更多