¿Cómo acceder al estado dentro del reductor Redux?

86

Tengo un reductor, y para calcular el nuevo estado necesito datos de la acción y también datos de una parte del estado no administrada por este reductor. Específicamente, en el reductor que mostraré a continuación, necesito acceso al accountDetails.stateOfResidenceIdcampo.

initialState.js:

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

        ]
    }
};

formulariosReducer.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 (reductor de raíz):

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

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

export default rootReducer;

¿Cómo puedo acceder a este campo?

Twilco
fuente
8
Esa afirmación no tiene ningún sentido. El objetivo de una función reductora es que usted toma decisiones basadas en el estado actual y la acción.
markerikson

Respuestas:

105

Me gustaría utilizar golpe seco para esto, he aquí un ejemplo:

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

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

Básicamente, obtiene todos los datos que necesita sobre la acción, luego puede enviar esos datos a su reductor.

Crysfel
fuente
4
¿Alguna idea de por qué el estado actual no está expuesto al reductor por el propio REDUX ..? parece extraño que al enviar necesito agregar cosas irrelevantes al reductor solo para que el estado inicial no anule el estado actual de otras cosas no relacionadas con la llamada de envío actual
vsync
10

Sus opciones son escribir más lógica además del uso de combineReducers, o incluir más datos en la acción. Las preguntas frecuentes de Redux cubren este tema:

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

Además, actualmente estoy trabajando en un nuevo conjunto de páginas de los documentos de Redux sobre el tema "Estructuración de reductores", que puede resultarle útil. Las páginas WIP actuales se encuentran en https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StructuringReducers.md .

markerikson
fuente
Gracias por tu respuesta. He explorado algunos de los recursos que ha vinculado y continuaré haciéndolo. Sin embargo, pregunta para ti, ya que pareces estar bien informado. Otra respuesta sugirió el uso de thunk. ¿Crees que esta sería una buena solución a mi problema? Si va a estropear mi código o no es una buena práctica, no es una solución que quiero buscar, pero no me siento lo suficientemente informado como para determinar los efectos a largo plazo de estas elecciones.
twilco
3
Sí, los procesadores son el enfoque básico estándar para administrar los efectos secundarios y ejecutar una lógica compleja que involucra múltiples envíos y el uso del estado actual de la aplicación. Es totalmente normal escribir una función de procesador que lea el estado actual de la aplicación y haga cosas como enviar condicionalmente, enviar múltiples acciones o tomar parte del estado e incluirlo en una acción enviada. Tengo algunos ejemplos de patrones de procesador comunes en gist.github.com/markerikson/ea4d0a6ce56ee479fe8b356e099f857e .
markerikson
2

No estoy seguro de si este enfoque es un anti-patrón, pero funcionó para mí. Utilice una función curry en sus acciones.

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

Es simple escribir su propia función de combinación que hace exactamente lo que desea:

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; 

Su FormReducer sería entonces:

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

FormsReducer ahora tiene acceso a accountDetails.

El beneficio de este enfoque es que solo expone las secciones del estado que necesita, en lugar de todo el estado.

GhostEcho
fuente
0

Le sugiero que lo pase al creador de acciones. Entonces, en algún lugar tendrás un creador de acciones que hace algo como esto:

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

En el lugar donde desencadena la acción, asuma que está usando react, puede usar

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

y conéctese a su componente de reacción usando la conexión de react-redux.

connect(mapStateToProps)(YourReactComponent);

Ahora, en su componente de reacción donde desencadena la acción updateProduct, debe tener stateOfResidenceId como prop, y puede pasarlo a su creador de acciones.

Suena complicado, pero en realidad se trata de una separación de preocupaciones.

Xing Wang
fuente
0

Puedes intentar usar:

redux-named-redurs

Lo que le permite obtener el estado en cualquier lugar de su código de esta manera:

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

Pero piense primero si sería mejor pasar el estado externo como carga útil en la acción.

miles_cristiano
fuente
0

Una forma alternativa, si usa react-redux y necesita esa acción solo en un lugar O está bien con la creación de un HOC (componente superior o superior, realmente no necesita comprender que lo importante es que esto podría inflar su html) en todos los lugares que necesite ese acceso es usar mergeprops con los parámetros adicionales que se pasan a la acción:

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)

Cuando use mergeProps, lo que devuelva se agregará a los accesorios, mapState y mapDispatch solo servirán para proporcionar los argumentos para mergeProps. Entonces, en otras palabras, esta función agregará esto a los accesorios de su componente (sintaxis de mecanografiado):

{hydratedUpdateProduct: () => void}

(tenga en cuenta que la función en realidad devuelve la acción en sí y no la anula, pero lo ignorará en la mayoría de los casos).

Pero lo que puedes hacer es:

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

esto proporcionará lo siguiente a los accesorios:

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

En general, aunque la desaprobación total que muestran los mantenedores de redux de expandir la funcionalidad de su paquete para incluir tales deseos de una buena manera, lo que crearía un patrón para estas funcionalidades SIN apoyar la fragmentación del ecosistema, es impresionante.

Los paquetes como Vuex que no son tan obstinados no tienen tantos problemas con las personas que abusan de los antipatrones porque se pierden, al mismo tiempo que admiten una sintaxis más limpia con menos repetición de la que jamás podrá archivar con redux y los mejores paquetes de soporte. Y a pesar de que el paquete es mucho más versátil, la documentación es más fácil de entender porque no se pierden en los detalles, como suele ocurrir con la documentación de reduxs.

Sam96
fuente
-1

Mientras distribuye una acción, puede pasar un parámetro. En este caso, puede pasar accountDetails.stateOfResidenceIda la acción y luego pasarla al reductor como carga útil.

ddavy
fuente