Jak uzyskać dostęp do stanu wewnątrz reduktora Redux?

86

Posiadam reduktor i do wyliczenia nowego stanu potrzebuję danych z akcji oraz danych z części stanu nie zarządzanej przez ten reduktor. Konkretnie w reduktorze pokażę poniżej potrzebuję dostępu do accountDetails.stateOfResidenceIdpola.

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

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

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

export default rootReducer;

Jak mogę uzyskać dostęp do tego pola?

twilco
źródło
8
To stwierdzenie nie ma żadnego sensu. Celem funkcji reduktora jest podejmowanie decyzji na podstawie bieżącego stanu i akcji.
markerikson

Odpowiedzi:

105

Użyłbym thunk o tym, oto przykład:

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

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

Zasadniczo otrzymujesz wszystkie potrzebne dane dotyczące akcji, a następnie możesz wysłać te dane do reduktora.

Crysfel
źródło
4
Każdy pomysł, dlaczego aktualny stan nie jest narażony na działanie reduktora przez sam REDUX ..? wydaje się dziwne, że podczas wysyłania muszę dodać nieistotne rzeczy do reduktora, aby stan początkowy nie zastąpił bieżącego stanu innych niezwiązanych rzeczy z bieżącym wywołaniem wysyłki
vsync
10

Masz do wyboru napisanie większej ilości logiki poza zwykłym użyciem combineReducerslub włączenie większej ilości danych do akcji. FAQ Redux obejmuje ten temat:

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

Obecnie pracuję również nad nowym zestawem stron do dokumentacji Redux na temat „Strukturyzujących Reduktorów”, które mogą okazać się pomocne. Aktualne strony WIP znajdują się pod adresem https://github.com/markerikson/redux/blob/structuring-reducers-page/docs/recipes/StruifyingReducers.md .

markerikson
źródło
Dzięki za odpowiedź. Zbadałem niektóre zasoby, które połączyłeś, i nadal będę to robić. Jednak pytanie do ciebie, ponieważ wydajesz się być kompetentny. Inna odpowiedź sugerowała użycie thunk. Czy uważasz, że byłoby to dobre rozwiązanie mojego problemu? Jeśli ma narobić bałaganu w moim kodzie lub nie jest najlepszą praktyką, nie jest to rozwiązanie, do którego chciałbym dążyć, ale nie czuję się wystarczająco kompetentny, aby określić długoterminowe skutki tych wyborów.
twilco
3
Tak, thunks to standardowe podstawowe podejście do zarządzania skutkami ubocznymi i wykonywania złożonej logiki, która obejmuje wiele wysyłek i używanie bieżącego stanu aplikacji. Jest całkowicie normalne pisanie funkcji thunk, która odczytuje bieżący stan aplikacji i wykonuje takie czynności, jak wysyłanie warunkowe, wysyłanie wielu akcji lub pobieranie części stanu i uwzględnianie tego w akcji wysłanej. Mam kilka przykładów typowych wzorców Thunk na gist.github.com/markerikson/ea4d0a6ce56ee479fe8b356e099f857e .
markerikson
2

Nie jestem pewien, czy to podejście jest anty-wzorcem, ale zadziałało w moim przypadku. Używaj funkcji curry w swoich działaniach.

export const myAction = (actionData) => (dispatch, getState) => {
   dispatch({
      type: 'SOME_ACTION_TYPE',
      data: actionData,
      state: getState()
   });
}
user3377090
źródło
1

Łatwo jest napisać własną funkcję łączenia, która robi dokładnie to, co chcesz:

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; 

Twój FormReducer miałby wtedy postać:

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

FormsReducer ma teraz dostęp do accountDetails.

Zaletą tego podejścia jest to, że eksponujesz tylko fragmenty stanu, których potrzebujesz, zamiast całego stanu.

GhostEcho
źródło
0

Sugeruję, abyś przekazał to kreatorowi akcji. Więc gdzieś będziesz mieć kreatora akcji, który robi coś takiego:

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

W miejscu, w którym wyzwalasz akcję, załóż, że używasz Reaguj, możesz użyć

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

i połącz się ze swoim komponentem Reaguj za pomocą połączenia React-Redux.

connect(mapStateToProps)(YourReactComponent);

Teraz w komponencie reagowania, w którym uruchamiasz akcję updateProduct, powinieneś mieć atrybut stateOfResidenceId i możesz przekazać go do swojego kreatora akcji.

Brzmi zagmatwane, ale tak naprawdę chodzi o oddzielenie obaw.

Xing Wang
źródło
0

Możesz spróbować użyć:

reduktory o nazwie redux

Co pozwala uzyskać stan w dowolnym miejscu w kodzie, na przykład:

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

Ale zastanów się najpierw, czy lepiej byłoby przekazać stan zewnętrzny jako ładunek w akcji.

miles_christian
źródło
0

Alternatywny sposób, jeśli używasz React-Redux i potrzebujesz tego działania tylko w jednym miejscu LUB możesz stworzyć HOC (komponent o wyższej jakości, naprawdę nie musisz rozumieć, że ważne jest to, że może to nadmuchać twój HTML) wszędzie, gdzie potrzebujesz dostęp polega na użyciu mergeprops z dodatkowymi parametrami przekazywanymi do akcji:

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)

Kiedy używasz mergeProps, to, co zwrócisz, zostanie dodane do właściwości, mapState i mapDispatch posłużą tylko jako argumenty dla mergeProps. Innymi słowy, ta funkcja doda to do twoich właściwości komponentu (składnia maszynopisu):

{hydratedUpdateProduct: () => void}

(zwróć uwagę, że funkcja w rzeczywistości zwraca samą akcję, a nie void, ale w większości przypadków zignorujesz to).

Ale możesz zrobić:

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

to dostarczy następujące rzeczy do rekwizytów:

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

Ogólnie rzecz biorąc, chociaż całkowite zaniedbanie, które opiekunowie redux wykazują, aby rozszerzyć funkcjonalność swojego pakietu tak, aby zawierał takie życzenia w dobry sposób, który stworzyłby wzorzec dla tych funkcji BEZ wspierania fragmentacji ekosystemu, robi wrażenie.

Pakiety takie jak Vuex, które nie są tak uparte, nie mają prawie tak wielu problemów z ludźmi nadużywającymi antywzorców, ponieważ gubią się, jednocześnie wspierając czystszą składnię z mniejszą liczbą szablonów niż kiedykolwiek archiwizujesz za pomocą Redux i najlepszych pakietów pomocniczych. I pomimo tego, że pakiet jest znacznie bardziej wszechstronny, dokumentacja jest łatwiejsza do zrozumienia, ponieważ nie gubi się w szczegółach, takich jak dokumentacja Redux.

Sam96
źródło
-1

Wysyłając akcję, możesz przekazać parametr. W takim przypadku możesz przejść accountDetails.stateOfResidenceIddo akcji, a następnie przekazać ją do reduktora jako ładunek.

ddavy
źródło