TL; DR
Bez combineReducers()
lub podobnego kodu ręcznego initialState
zawsze wygrywa state = ...
w reduktorze, ponieważ state
przekazany do reduktora jest initialState
i nie jest undefined
, więc składnia argumentu ES6 nie jest stosowana w tym przypadku.
Dzięki combineReducers()
zachowaniu jest bardziej zniuansowany. Te reduktory, których stan jest określony w initialState
, otrzymają to state
. Inne reduktory otrzymają undefined
iz tego powodu powrócą do state = ...
domyślnego argumentu, który określą.
Generalnie initialState
wygrywa stan określony przez reduktor. Pozwala to reduktorom określić początkowe dane, które mają dla nich sens jako domyślne argumenty, ale także umożliwia ładowanie istniejących danych (w całości lub częściowo) podczas nawadniania sklepu z trwałej pamięci masowej lub serwera.
Najpierw rozważmy przypadek, w którym masz pojedynczy reduktor.
Powiedz, że nie używasz combineReducers()
.
Wtedy twój reduktor może wyglądać tak:
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT': return state + 1;
case 'DECREMENT': return state - 1;
default: return state;
}
}
Teraz powiedzmy, że tworzysz z nim sklep.
import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState());
Stan początkowy wynosi zero. Czemu? Ponieważ drugim argumentem createStore
było undefined
. To jest state
przekazane do twojego reduktora za pierwszym razem. Podczas inicjalizacji Redux wysyła „fikcyjną” akcję, aby wypełnić stan. Więc twój counter
reduktor został wywołany z state
równą undefined
. Dokładnie tak jest, gdy „aktywuje” domyślny argument. Dlatego state
jest teraz 0
zgodnie z state
wartością domyślną ( state = 0
). Ten stan ( 0
) zostanie zwrócony.
Rozważmy inny scenariusz:
import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState());
Dlaczego tym razem tak jest 42
, a nie 0
? Ponieważ createStore
został wywołany 42
jako drugi argument. Ten argument jest state
przekazywany do twojego reduktora wraz z fikcyjną akcją. Tym razem state
nie jest niezdefiniowana (jest 42
!), Więc domyślna składnia argumentów ES6 nie ma znaczenia. state
Jest 42
, i 42
jest zwrócony od reduktora.
Rozważmy teraz przypadek, w którym używasz combineReducers()
.
Masz dwa reduktory:
function a(state = 'lol', action) {
return state;
}
function b(state = 'wat', action) {
return state;
}
Reduktor generowany przez combineReducers({ a, b })
wygląda następująco:
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
};
}
Jeśli zadzwonimy createStore
bez tego initialState
, zainicjuje state
to {}
. Dlatego state.a
i state.b
będzie undefined
do czasu, gdy wzywa a
i b
redukuje. Oba a
i b
reduktory otrzymają undefined
jako swoje state
argumenty, a jeśli określą state
wartości domyślne , zostaną one zwrócone. W ten sposób połączony reduktor zwraca { a: 'lol', b: 'wat' }
obiekt stanu przy pierwszym wywołaniu.
import { createStore } from 'redux';
let store = createStore(combined);
console.log(store.getState());
Rozważmy inny scenariusz:
import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState());
Teraz określiłem initialState
jako argument do createStore()
. Stan zwrócony z połączonego reduktora łączy stan początkowy określony dla a
reduktora z 'wat'
domyślnym argumentem określonym, który b
sam wybrał reduktor.
Przypomnijmy, co robi kombinowany reduktor:
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
};
}
W tym przypadku state
został określony, więc nie powrócił do {}
. Był to obiekt o a
polu równym 'horse'
, ale bez b
pola. Dlatego a
reduktor otrzymał 'horse'
jako swój state
i chętnie go zwrócił, ale b
reduktor otrzymał undefined
jako swój state
i w ten sposób zwrócił swoją ideę domyślną state
(w naszym przykładzie 'wat'
). Tak otrzymujemy { a: 'horse', b: 'wat' }
w zamian.
Podsumowując tę górę, jeśli trzymać się konwencji Redux i przywrócić stan początkowy z reduktorów, kiedy nazywa się je ze undefined
jako state
argumentu (najprostszym sposobem realizacji tego celu jest określenie state
wartości domyślne argumentów ES6), będziesz mieć ładne użyteczne zachowanie dla połączonych reduktorów. Będą preferować odpowiednią wartość w initialState
obiekcie, który przekazujesz do createStore()
funkcji, ale jeśli nie przekazałeś żadnej lub jeśli odpowiednie pole nie jest ustawione, state
zamiast tego wybierany jest domyślny argument określony przez reduktor.To podejście działa dobrze, ponieważ zapewnia zarówno inicjalizację, jak i hydratację istniejących danych, ale umożliwia poszczególnym reduktorom resetowanie ich stanu, jeśli ich dane nie zostały zachowane. Oczywiście możesz zastosować ten wzorzec rekurencyjnie, ponieważ możesz go używać combineReducers()
na wielu poziomach, a nawet ręcznie tworzyć redukcje, wywołując redukcje i dając im odpowiednią część drzewa stanu.
combineReducers
. Jeszcze raz bardzo dziękuję.function counter(state = 0, action)
Lubfunction visibleIds(state = [], action)
.Krótko mówiąc: to Redux przekazuje stan początkowy reduktorom, nie musisz nic robić.
Kiedy dzwonisz
createStore(reducer, [initialState])
, informujesz Redux, jaki jest stan początkowy, który ma zostać przekazany do reduktora, gdy nadejdzie pierwsza akcja.Druga opcja, o której wspomniałeś, ma zastosowanie tylko w przypadku, gdy nie przeszedłeś stanu początkowego podczas tworzenia sklepu. to znaczy
function todoApp(state = initialState, action)
stan zostanie zainicjowany tylko wtedy, gdy żaden stan nie został przekazany przez Redux
źródło
menuState
, jak mam powiedzieć reduktorowi menu, aby odczytał wartośćstate.menu
ze sklepu i użył tego jako stanu początkowego?connectReducers () wykonują pracę za Ciebie. Pierwszy sposób, aby to napisać, nie jest zbyt pomocny:
const rootReducer = combineReducers({ todos, users })
Ale druga, równoważna, jest bardziej jasna:
function rootReducer(state, action) { todos: todos(state.todos, action), users: users(state.users, action) }
źródło
Mam nadzieję, że to odpowiedź na Twoją prośbę (którą rozumiałem jako inicjowanie reduktorów podczas przechodzenia przez stan intialState i zwracania tego stanu)
Tak to robimy (uwaga: skopiowano z kodu Typescript).
Istotą tego jest
if(!state)
test w funkcji mainReducer (fabrycznej)function getInitialState(): MainState { return { prop1: 'value1', prop1: 'value2', ... } } const reducer = combineReducers( { main: mainReducer( getInitialState() ), ... } ) const mainReducer = ( initialState: MainState ): Reducer => { return ( state: MainState, action: Action ): MainState => { if ( !state ) { return initialState } console.log( 'Main reducer action: ', action ) switch ( action.type ) { .... } } }
źródło