web-dev-qa-db-fra.com

Comment accéder à l'état à l'intérieur du réducteur Redux?

J'ai un réducteur, et afin de calculer le nouvel état, j'ai besoin de données de l'action et de données d'une partie de l'état non gérée par ce réducteur. Plus précisément, dans le réducteur que je montrerai ci-dessous, il me faut un accès au champ 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 (réducteur de racine):

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

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

export default rootReducer;

Comment puis-je accéder à ce champ?

50
twilco

Je voudrais utiliser thunk pour cela, voici un exemple:

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

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

En gros, vous obtenez toutes les données dont vous avez besoin sur l'action, vous pouvez ensuite les envoyer à votre réducteur.

70
Crysfel

Vous avez le choix entre écrire davantage de logique en plus d'utiliser combineReducers ou inclure plus de données dans l'action. Le Redux FAQ couvre ce sujet: http://redux.js.org/docs/faq/Reducers.html#reducers-share-state .

De plus, je travaille actuellement sur un nouvel ensemble de pages de la documentation Redux sur le thème "Réducteurs de structuration", qui pourraient vous être utiles. Les pages WIP actuelles sont à https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StructuringReducers.md .

8
markerikson

Une autre façon, si vous utilisez react-redux et n’avez besoin de cette action qu’à un seul endroit OR vous permet de créer un HOC (composant supérieur ou supérieur, n’avez pas vraiment besoin de comprendre que l’important, c’est que cela risque de gonfler votre html) partout où vous avez besoin de cet accès, utilisez mergeprops avec les paramètres supplémentaires passés à l'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)

MapState et mapDispatch ne serviront qu'à fournir les arguments de mergeProps lorsque vous utiliserez mergeProps. Donc, en d'autres termes, cette fonction ajoutera ceci à vos propriétés de composant (syntaxe TypeScript):

{hydratedUpdateProduct: () => void}

(notez que la fonction renvoie en réalité l’action elle-même et non la nullité, mais vous l’ignorerez dans la plupart des cas).

Mais ce que vous pouvez faire c'est:

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

cela fournira les choses suivantes aux accessoires:

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

Dans l’ensemble, bien que le désaccord total des responsables de la maintenance redux montre qu’il est impressionnant d’étendre les fonctionnalités de leur paquet pour inclure de tels souhaits, ce qui créerait un modèle pour ces fonctionnalités SANS la fragmentation de l’écosystème.

Les paquets comme Vuex qui ne sont pas si obstinés n'ont pas presque autant de problèmes avec ceux qui abusent des antipatternes parce qu'ils sont perdus, tout en supportant une syntaxe plus propre avec moins de passe-partout que vous n'archiverez jamais avec redux et les meilleurs paquets de support. Et bien que le paquet soit beaucoup plus polyvalent, la documentation est plus facile à comprendre car elle ne se perd pas dans les détails comme le fait la documentation de reduxs.

0
Sam96

Je suggère que vous le transmettiez au créateur de l'action. Donc vous aurez un créateur d’action qui fait quelque chose comme ça: 

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

À l’endroit où vous déclenchez l’action, supposez que vous utilisez réagissez, vous pouvez utiliser 

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

et connectez-vous à votre composant react en utilisant la connexion de react-redux. 

connect(mapStateToProps)(YourReactComponent);

Maintenant, dans votre composant react où vous déclenchez l'action updateProduct, vous devez avoir l'état stateOfResidenceId comme accessoire, et vous pouvez le transmettre à votre créateur d'action. 

Cela semble compliqué, mais il s’agit vraiment de séparer les préoccupations. 

0
Xing Wang

Vous pouvez essayer d'utiliser:

Réducteurs de noms redux

Ce qui vous permet d'obtenir l'état n'importe où dans votre code, comme ceci:

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

Mais pensons d’abord s’il serait préférable de passer l’état externe comme une charge utile dans l’action. 

0
miles_christian

Je ne sais pas si cette approche est un anti-modèle, mais cela a fonctionné pour moi. Utilisez une fonction au curry dans vos actions.

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

Il est simple d’écrire votre propre fonction de combinaison qui fait exactement ce que vous voulez:

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; 

Votre FormReducer serait alors:

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

FormsReducer a maintenant accès à accountDetails.

L'avantage de cette approche est que vous n'exposez que les tranches d'état dont vous avez besoin, au lieu de l'état entier.

0
GhostEcho