Wie greife ich auf den Status im Redux-Reduzierer zu?

88

Ich habe einen Reduzierer, und um den neuen Status zu berechnen, benötige ich Daten aus der Aktion sowie Daten aus einem Teil des Status, der nicht von diesem Reduzierer verwaltet wird. Insbesondere in dem unten gezeigten Reduzierer benötige ich Zugriff auf das accountDetails.stateOfResidenceIdFeld.

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 (Root Reducer):

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

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

export default rootReducer;

Wie kann ich auf dieses Feld zugreifen?

Kibowki
quelle
8
Diese Aussage macht keinen Sinn. Der springende Punkt einer Reduzierungsfunktion ist, dass Sie Entscheidungen basierend auf dem aktuellen Status und der Aktion treffen .
Markerikson

Antworten:

105

Ich würde dafür Thunk verwenden , hier ein Beispiel:

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

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

Grundsätzlich erhalten Sie alle Daten, die Sie für die Aktion benötigen, und können diese Daten dann an Ihren Reduzierer senden.

Crysfel
quelle
4
Irgendeine Idee, warum der aktuelle Zustand nicht durch REDUX selbst dem Reduzierer ausgesetzt ist ..? Es scheint seltsam, dass ich beim Versenden irrelevante Dinge zum Reduzierer hinzufügen muss, damit der Anfangszustand den aktuellen Zustand anderer nicht verwandter
Dinge
10

Sie können entweder mehr Logik schreiben als nur verwenden combineReducersoder mehr Daten in die Aktion einbeziehen. Die Redux-FAQ behandelt dieses Thema:

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

Außerdem arbeite ich derzeit an einer neuen Reihe von Seiten in den Redux-Dokumenten zum Thema "Strukturieren von Reduzierern", die Sie möglicherweise hilfreich finden. Die aktuellen WIP-Seiten finden Sie unter https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StructuringReducers.md .

markerikson
quelle
Danke für deine Antwort. Ich habe einige der von Ihnen verknüpften Ressourcen untersucht und werde dies auch weiterhin tun. Frage an Sie jedoch, da Sie sachkundig zu sein scheinen. Eine andere Antwort schlug die Verwendung von vor thunk. Glaubst du, das wäre eine gute Lösung für mein Problem? Wenn es meinen Code durcheinander bringt oder keine bewährte Methode ist, ist es keine Lösung, die ich verfolgen möchte, aber ich fühle mich nicht gut genug informiert, um die langfristigen Auswirkungen dieser Entscheidungen zu bestimmen.
Kibowki
3
Ja, Thunks sind der grundlegende Standardansatz zum Verwalten von Nebenwirkungen und zum Ausführen komplexer Logik, die mehrere Versendungen umfasst und den aktuellen App-Status verwendet. Es ist völlig normal, eine Thunk-Funktion zu schreiben, die den aktuellen App-Status liest und Dinge wie bedingtes Versenden, Versenden mehrerer Aktionen oder Abrufen eines Teils des Status ausführt und dies in eine versendete Aktion einbezieht. Ich habe einige Beispiele für gängige Thunk-Muster unter gist.github.com/markerikson/ea4d0a6ce56ee479fe8b356e099f857e .
markerikson
2

Ich bin mir nicht sicher, ob dieser Ansatz ein Anti-Muster ist, aber er hat bei mir funktioniert. Verwenden Sie eine Curry-Funktion in Ihren Aktionen.

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

Es ist einfach, eine eigene Kombinationsfunktion zu schreiben, die genau das tut, was Sie wollen:

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; 

Ihr FormReducer wäre dann:

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

Der FormsReducer hat jetzt Zugriff auf die accountDetails.

Der Vorteil dieses Ansatzes besteht darin, dass Sie statt des gesamten Zustands nur die von Ihnen benötigten Zustandsscheiben verfügbar machen.

GhostEcho
quelle
0

Ich schlage vor, dass Sie es an den Action Creator weitergeben. Irgendwo haben Sie also einen Action Creator, der so etwas macht:

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

Angenommen, Sie verwenden "Reagieren" an der Stelle, an der Sie die Aktion auslösen

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

und verbinden Sie sich mit Ihrer Reaktionskomponente über die Verbindung des React-Redux.

connect(mapStateToProps)(YourReactComponent);

In Ihrer Reaktionskomponente, in der Sie das Aktionsaktualisierungsprodukt auslösen, sollten Sie nun die stateOfResidenceId als Requisite haben und diese an Ihren Aktionsersteller übergeben können.

Es klingt verworren, aber es geht wirklich um die Trennung von Bedenken.

Xing Wang
quelle
0

Sie können versuchen, Folgendes zu verwenden:

Redux-Named-Reducer

So können Sie den Status überall in Ihrem Code wie folgt abrufen:

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

Aber denken Sie zuerst darüber nach, ob es besser wäre, den externen Zustand als Nutzlast in der Aktion zu übergeben.

Miles_christian
quelle
0

Eine alternative Möglichkeit, wenn Sie React-Redux verwenden und diese Aktion nur an einer Stelle benötigen, ODER wenn Sie ein HOC (Higher oder Component, müssen Sie nicht wirklich verstehen, dass das Wichtigste darin besteht, dass dies Ihr HTML aufblähen könnte) überall dort sind, wo Sie es benötigen Bei diesem Zugriff werden Mergeprops verwendet, wobei die zusätzlichen Parameter an die Aktion übergeben werden:

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)

Wenn Sie mergeProps verwenden, wird das, was Sie dort zurückgeben, zu den Requisiten hinzugefügt. MapState und mapDispatch dienen nur dazu, die Argumente für mergeProps bereitzustellen. Mit anderen Worten, diese Funktion fügt dies Ihren Komponenten-Requisiten hinzu (Typoskript-Syntax):

{hydratedUpdateProduct: () => void}

(Beachten Sie, dass die Funktion die Aktion selbst zurückgibt und nicht ungültig ist. In den meisten Fällen wird dies jedoch ignoriert.)

Aber was Sie tun können, ist:

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

Dadurch erhalten die Requisiten Folgendes:

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

Insgesamt ist die völlige Ablehnung, die die Redux-Betreuer zeigen, um die Funktionalität ihres Pakets zu erweitern, um solche Wünsche auf eine gute Weise zu berücksichtigen, die ein Muster für diese Funktionen ohne Unterstützung der Fragmentierung des Ökosystems schaffen würde, beeindruckend.

Pakete wie Vuex, die nicht so hartnäckig sind, haben nicht annähernd so viele Probleme mit Menschen, die Antimuster missbrauchen, weil sie verloren gehen, und unterstützen eine sauberere Syntax mit weniger Boilerplate als jemals zuvor mit Redux und den besten unterstützenden Paketen. Und obwohl das Paket viel vielseitiger ist, ist die Dokumentation leichter zu verstehen, da sie nicht in den Details verloren geht, wie dies bei der Redux-Dokumentation der Fall ist.

Sam96
quelle
-1

Während Sie eine Aktion auslösen, können Sie einen Parameter übergeben. In diesem Fall können Sie accountDetails.stateOfResidenceIdan die Aktion übergeben und sie dann als Nutzlast an den Reduzierer weitergeben.

ddavy
quelle