如何访问减速器内部的状态?

我有一个 reducer,为了计算新的状态,我需要来自操作的数据,还需要来自不由这个 reducer 管理的状态的一部分的数据。具体来说,在下面我将展示的减速器中,我需要访问 accountDetails.stateOfResidenceId字段。

Js:

export default {
accountDetails: {
stateOfResidenceId: '',
accountType: '',
accountNumber: '',
product: ''
},
forms: {
blueprints: [


]
}
};

表格 reducer.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;
}
}

Js (根减速器) :

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


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


export default rootReducer;

我如何访问这个字段?

73477 次浏览

While dispatching an action, you can pass a parameter. In this case, you could pass accountDetails.stateOfResidenceId to the action and then pass it on to the reducer as payload.

Your options are to either write more logic besides just use of combineReducers, or include more data in the action. The Redux FAQ covers this topic:

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

Also, I'm currently working on a new set of pages to the Redux docs on the topic of "Structuring Reducers", which you may find helpful. The current WIP pages are at https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StructuringReducers.md .

I sugguest that you pass it into the action creator. So someplace you'll have an action creator that does something like this:

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

At the place where you trigger the action, assume you are using react, you can use

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

and connect to your react component using the react-redux's connect.

connect(mapStateToProps)(YourReactComponent);

Now, in your react component where you trigger the action updateProduct, you should have the stateOfResidenceId as a prop, and you can pass it to your action creator.

It sounds convoluted, but it is really about separation of concerns.

I would use thunk for this, here's an example:

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


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

Basically you get all the data you need on the action, then you can send that data to your reducer.

You can try to use:

redux-named-reducers

Which allows you to get state anywhere in your code like so:

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

But think first if it would be better to pass the external state as a payload in the action.

I'm not sure if this approach is an anti-pattern but it worked for me. Use a curried function in your actions.

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

It is simple to write your own combine function that does exactly what you want:

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;

Your FormReducer would then be:

export default function formsReducer(state = initialState.forms, action, accountDetails) {

The formsReducer now has access to the accountDetails.

The benefit of this approach is that you only expose the slices of state the you need, instead of the entire state.

An alternative way, if you use react-redux and need that action only in one place OR are fine with creating an HOC (Higher oder component, dont really need to understand that the important stuff is that this might bloat your html) everywhere you need that access is to use mergeprops with the additional parameters being passed to the action:

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)

When you use mergeProps what you return there will be added to the props, mapState and mapDispatch will only serve to provide the arguments for mergeProps. So, in other words, this function will add this to your component props (typescript syntax):

{hydratedUpdateProduct: () => void}

(take note that the function actually returns the action itself and not void, but you'll ignore that in most cases).

But what you can do is:

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 will provide the following stuff to the props:

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

On the whole though the complete dissaproval the redux-maintainers show to expanding the functionality of their package to include such wishes in a good way, which would create a pattern for these functionalities WITHOUT supporting fragmentation of the ecosystem, is impressive.

Packages like Vuex that are not so stubborn dont have nearly so many issues with people abusing antipatterns because they get lost, while supporting a way cleaner syntax with less boilerplate than you'll ever archive with redux and the best supporting packages. And despite the package being way more versatile the documantation is easier to understand because they dont get lost in the details like reduxs documentation tends to do.

If you have access to store then you should use store.getState().

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