TL; DR
Sin combineReducers()
código manual o similar, initialState
siempre gana state = ...
en el reductor porque lo que se state
pasa al reductor es initialState
y no es undefined
, por lo que la sintaxis del argumento ES6 no se aplica en este caso.
Con combineReducers()
el comportamiento es más matizado. Los reductores cuyo estado se especifica en initialState
recibirán eso state
. Otros reductores recibirán undefined
y, debido a eso , volverán al state = ...
argumento predeterminado que especifican.
En general, initialState
gana el estado especificado por el reductor. Esto permite que los reductores especifiquen datos iniciales que tengan sentido para ellos como argumentos predeterminados, pero también permite cargar datos existentes (total o parcialmente) cuando está hidratando la tienda desde algún almacenamiento persistente o el servidor.
Primero, consideremos un caso en el que tiene un solo reductor.
Di que no usas combineReducers()
.
Entonces su reductor podría verse así:
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT': return state + 1;
case 'DECREMENT': return state - 1;
default: return state;
}
}
Ahora digamos que crea una tienda con él.
import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState());
El estado inicial es cero. ¿Por qué? Porque el segundo argumento de createStore
fue undefined
. Este es el state
pasado a su reductor la primera vez. Cuando Redux se inicializa, envía una acción "ficticia" para completar el estado. Entonces su counter
reductor fue llamado state
igual a undefined
. Este es exactamente el caso que "activa" el argumento predeterminado. Por lo tanto, state
ahora tiene 0
el state
valor predeterminado ( state = 0
). Se 0
devolverá este estado ( ).
Consideremos un escenario diferente:
import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState());
¿Por qué es 42
y no 0
esta vez? Porque createStore
fue llamado con 42
como segundo argumento. Este argumento se state
pasa a su reductor junto con la acción ficticia. Esta vez, state
no está indefinido (¡lo está 42
!), Por lo que la sintaxis del argumento predeterminado de ES6 no tiene ningún efecto. El state
es 42
y 42
se devuelve del reductor.
Ahora consideremos un caso en el que usa combineReducers()
.
Tienes dos reductores:
function a(state = 'lol', action) {
return state;
}
function b(state = 'wat', action) {
return state;
}
El reductor generado por combineReducers({ a, b })
tiene este aspecto:
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
};
}
Si llamamos createStore
sin el initialState
, se inicializará el state
a {}
. Por tanto, state.a
y state.b
será undefined
en el momento en que llame a
y b
reduzca. Tanto a
y b
reductores recibirán undefined
como sus state
argumentos, y si especifican por defecto state
los valores, los que serán devueltos. Así es como el reductor combinado devuelve un { a: 'lol', b: 'wat' }
objeto de estado en la primera invocación.
import { createStore } from 'redux';
let store = createStore(combined);
console.log(store.getState());
Consideremos un escenario diferente:
import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState());
Ahora especifiqué initialState
como argumento createStore()
. El estado devuelto por el reductor combinado combina el estado inicial que especifiqué para el a
reductor con el 'wat'
argumento predeterminado especificado que el b
reductor eligió a sí mismo.
Recordemos lo que hace el reductor combinado:
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
};
}
En este caso, state
se especificó para no volver a {}
. Era un objeto con a
campo igual a 'horse'
, pero sin b
campo. Es por eso que el a
reductor lo recibió 'horse'
como state
suyo y lo devolvió con gusto, pero el b
reductor lo recibió undefined
como suyo state
y, por lo tanto, devolvió su idea del valor predeterminado state
(en nuestro ejemplo, 'wat'
). Así es como recibimos { a: 'horse', b: 'wat' }
a cambio.
En resumen, si se apega a las convenciones de Redux y devuelve el estado inicial de los reductores cuando se llaman con undefined
el state
argumento (la forma más fácil de implementar esto es especificar el state
valor del argumento predeterminado de ES6), tendrá un buen comportamiento útil para reductores combinados. Preferirán el valor correspondiente en el initialState
objeto que pasa a la createStore()
función, pero si no pasó ninguno, o si el campo correspondiente no está configurado, state
se elige el argumento predeterminado especificado por el reductor.Este enfoque funciona bien porque proporciona tanto la inicialización como la hidratación de los datos existentes, pero permite que los reductores individuales restablezcan su estado si sus datos no se conservaron. Por supuesto, puede aplicar este patrón de forma recursiva, como puede utilizar combineReducers()
en muchos niveles, o incluso componer reductores manualmente llamando a los reductores y dándoles la parte relevante del árbol de estado.
combineReducers
. Muchisímas gracias de nuevo.