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.function counter(state = 0, action)
ofunction visibleIds(state = [], action)
.En pocas palabras: es Redux el que pasa el estado inicial a los reductores, no necesitas hacer nada.
Cuando llama,
createStore(reducer, [initialState])
le está haciendo saber a Redux cuál es el estado inicial que se pasará al reductor cuando entre la primera acción.La segunda opción que mencionas, se aplica solo en caso de que no hayas pasado un estado inicial al crear la tienda. es decir
function todoApp(state = initialState, action)
el estado solo se inicializará si no hubo ningún estado pasado por Redux
fuente
menuState
, ¿cómo le digo al reductor de menú que lea el valor destate.menu
de la tienda y lo use como su estado inicial?combineReducers () hace el trabajo por usted. La primera forma de escribirlo no es realmente útil:
const rootReducer = combineReducers({ todos, users })
Pero el otro, que es equivalente, es más claro:
function rootReducer(state, action) { todos: todos(state.todos, action), users: users(state.users, action) }
fuente
Espero que esto responda a su solicitud (que entendí como inicializar reductores al pasar intialState y devolver ese estado)
Así es como lo hacemos (advertencia: copiado del código Typecript).
La esencia es la
if(!state)
prueba en la función mainReducer (fábrica)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 ) { .... } } }
fuente