Come accedere allo stato all'interno del riduttore Redux?


88

Ho un riduttore e per calcolare il nuovo stato ho bisogno dei dati dell'azione e anche dei dati di una parte dello stato non gestita da questo riduttore. Nello specifico, nel riduttore che mostrerò di seguito, ho bisogno dell'accesso al accountDetails.stateOfResidenceIdcampo.

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 (riduttore di root):

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

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

export default rootReducer;

Come posso accedere a questo campo?



8
Questa affermazione non ha alcun senso. Il punto centrale di una funzione di riduzione è che si prendono decisioni in base allo stato corrente e all'azione.
markerikson

Risposte:


105

Vorrei usare thunk per questo, ecco un esempio:

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

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

Fondamentalmente ottieni tutti i dati di cui hai bisogno sull'azione, quindi puoi inviare quei dati al tuo riduttore.


4
Qualche idea sul perché lo stato attuale non sia esposto al riduttore da REDUX stesso ..? sembra strano che durante l'invio devo aggiungere cose irrilevanti al riduttore solo così lo stato iniziale non sovrascriverà lo stato corrente di altre cose non correlate alla chiamata di spedizione corrente
vsync

10

Le tue opzioni sono di scrivere più logica oltre al solo uso di combineReducers o includere più dati nell'azione. Le domande frequenti su Redux trattano questo argomento:

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

Inoltre, sto attualmente lavorando a una nuova serie di pagine della documentazione di Redux sull'argomento "Strutturazione dei riduttori", che potresti trovare utile. Le attuali pagine WIP si trovano su https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StructuringReducers.md .


Grazie per la tua risposta. Ho esplorato alcune delle risorse che hai collegato e continuerò a farlo. Tuttavia, domanda per te, dal momento che sembri essere informato. Un'altra risposta ha suggerito l'uso di thunk. Pensi che questa sarebbe una buona soluzione al mio problema? Se rovinerà il mio codice o non è una best practice, non è una soluzione che voglio perseguire, ma non mi sento abbastanza informato per determinare gli effetti a lungo termine di queste scelte.
kibowki

3
Sì, i thunk sono l'approccio di base standard per la gestione degli effetti collaterali e l'esecuzione di una logica complessa che coinvolge più invii e utilizza lo stato corrente dell'app. È del tutto normale scrivere una funzione thunk che legge lo stato dell'app corrente e esegue operazioni come inviare in modo condizionale, inviare più azioni o afferrare parte dello stato e includerlo in un'azione inviata. Ho alcuni esempi di modelli di thunk comuni su gist.github.com/markerikson/ea4d0a6ce56ee479fe8b356e099f857e .
markerikson

2

Non sono sicuro che questo approccio sia un anti-pattern, ma ha funzionato per me. Usa una funzione curry nelle tue azioni.

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

1

È semplice scrivere la tua funzione di combinazione che fa esattamente quello che vuoi:

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; 

Il tuo FormReducer sarebbe quindi:

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

Il formsReducer ora ha accesso all'accountDetails.

Il vantaggio di questo approccio è che esponi solo le sezioni di stato di cui hai bisogno, invece dell'intero stato.


0

Ti suggerisco di passarlo al creatore di azioni. Quindi da qualche parte avrai un creatore di azioni che fa qualcosa del genere:

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

Nel punto in cui attivi l'azione, supponi di usare Reagire, puoi usare

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

e connettiti al tuo componente react usando la connessione di react-redux.

connect(mapStateToProps)(YourReactComponent);

Ora, nel tuo componente react in cui attivi l'azione updateProduct, dovresti avere stateOfResidenceId come oggetto di scena e puoi passarlo al tuo creatore dell'azione.

Sembra contorto, ma in realtà si tratta di separazione delle preoccupazioni.


0

Puoi provare a usare:

riduttori con nome redux

Che ti consente di ottenere lo stato ovunque nel tuo codice in questo modo:

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

Ma prima pensa se sarebbe meglio passare lo stato esterno come carico utile nell'azione.


0

Un modo alternativo, se usi react-redux e hai bisogno di quell'azione solo in un posto OPPURE va bene con la creazione di un HOC (componente superiore oder, non hai davvero bisogno di capire che la cosa importante è che questo potrebbe gonfiare il tuo html) ovunque tu abbia bisogno tale accesso consiste nell'usare mergeprops con i parametri aggiuntivi passati all'azione:

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)

Quando usi mergeProps, ciò che restituisci verrà aggiunto agli oggetti di scena, mapState e mapDispatch serviranno solo a fornire gli argomenti per mergeProps. Quindi, in altre parole, questa funzione lo aggiungerà agli oggetti di scena del tuo componente (sintassi del dattiloscritto):

{hydratedUpdateProduct: () => void}

(tieni presente che la funzione restituisce effettivamente l'azione stessa e non è nulla, ma lo ignorerai nella maggior parte dei casi).

Ma quello che puoi fare è:

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

questo fornirà le seguenti cose agli oggetti di scena:

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

Nel complesso, sebbene il completo dissaproval che i manutentori di redux dimostrano di espandere la funzionalità del loro pacchetto per includere tali desideri in un buon modo, il che creerebbe un modello per queste funzionalità SENZA supportare la frammentazione dell'ecosistema, è impressionante.

Pacchetti come Vuex che non sono così testardi non hanno quasi così tanti problemi con le persone che abusano degli antipattern perché si perdono, mentre supportano una sintassi più pulita con meno boilerplate di quanto tu possa mai archiviare con redux ei migliori pacchetti di supporto. E nonostante il pacchetto sia molto più versatile, la documentazione è più facile da capire perché non si perdono nei dettagli come tende a fare la documentazione di redux.


-1

Durante l'invio di un'azione, puoi passare un parametro. In questo caso potresti passare accountDetails.stateOfResidenceIdall'azione e poi passarla al riduttore come carico utile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.