javascript - react - Lea el estado inicial de la tienda en Redux Reducer
redux-thunk (4)
TL; DR
Sin
combineReducers()
o código manual similar,initialState
siempre gana sobrestate = ...
en el reductor porque elstate
pasado al reductor esinitialState
y no está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 eninitialState
recibirán esestate
. Otros reductores recibiránundefined
y debido a eso volverán alstate = ...
argumento predeterminado que especifican.En general,
initialState
gana sobre el estado especificado por el reductor. Esto permite a los reductores especificar datos iniciales que tienen sentido para ellos como argumentos predeterminados, pero también permite cargar datos existentes (total o parcialmente) cuando se hidrata la tienda desde algún almacenamiento persistente o el servidor.
Primero consideremos un caso en el que tiene un solo reductor.
Digamos que no usa
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 supongamos que crea una tienda con él.
import { createStore } from ''redux'';
let store = createStore(counter);
console.log(store.getState()); // 0
El estado inicial es cero.
¿Por qué?
Porque el segundo argumento para
createStore
undefined
estaba
undefined
.
Este es el
state
pasado a su reductor la primera vez.
Cuando Redux se inicializa, envía una acción "ficticia" para llenar el estado.
Por lo tanto, su
counter
reductor se llamó con un
state
igual a
undefined
.
Este es exactamente el caso que "activa" el argumento predeterminado.
Por lo tanto, el
state
ahora es
0
según el valor de
state
predeterminado (
state = 0
).
Este estado (
0
) será devuelto.
Consideremos un escenario diferente:
import { createStore } from ''redux'';
let store = createStore(counter, 42);
console.log(store.getState()); // 42
¿Por qué es
42
, y no
0
, esta vez?
Porque se llamó a
createStore
con
42
como segundo argumento.
Este argumento se convierte en el
state
pasado a su reductor junto con la acción ficticia.
Esta vez, el
state
no está indefinido (¡es
42
!), Por lo que la sintaxis de argumento predeterminada de ES6 no tiene ningún efecto.
El
state
es
42
y el reductor devuelve
42
.
Ahora consideremos un caso en el que usas
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 })
ve así:
// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
};
}
Si llamamos a
createStore
sin
initialState
, inicializará el
state
a
{}
.
Por lo tanto,
state.a
y
state.b
estarán
undefined
en el momento en que llame
a
reductores
b
.
Los reductores
b
recibirán
undefined
como
sus
argumentos de
state
, y si especifican valores de
state
predeterminados, se devolverán.
Así es como el reductor combinado devuelve un objeto de estado
{ a: ''lol'', b: ''wat'' }
en la primera invocación.
import { createStore } from ''redux'';
let store = createStore(combined);
console.log(store.getState()); // { a: ''lol'', b: ''wat'' }
Consideremos un escenario diferente:
import { createStore } from ''redux'';
let store = createStore(combined, { a: ''horse'' });
console.log(store.getState()); // { a: ''horse'', b: ''wat'' }
Ahora especifiqué
initialState
como argumento para
createStore()
.
El estado devuelto por el reductor combinado
combina
el estado inicial que especifiqué para el reductor a con el argumento predeterminado
''wat''
que especifica que el reductor
b
eligió a sí mismo.
Recordemos lo que hace el reductor combinado:
// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
};
}
En este caso, se especificó el
state
para que no volviera a
{}
.
Era un objeto con
a
campo igual a
''horse''
, pero sin el campo
b
.
Es por eso que el reductor a recibió
''horse''
como su
state
y lo devolvió con gusto, pero el reductor
b
recibió
undefined
como su
state
y, por lo tanto, devolvió
su idea
del
state
predeterminado (en nuestro ejemplo,
''wat''
).
Así es como obtenemos
{ a: ''horse'', b: ''wat'' }
a cambio.
Para resumir esto, si se apega a las convenciones de Redux y devuelve el estado inicial de los reductores cuando se llaman con
undefined
como argumento de
state
(la forma más fácil de implementar esto es especificar el valor de argumento predeterminado del
state
ES6), está Va a tener un buen comportamiento útil para los reductores combinados.
Preferirán el valor correspondiente en el objeto
initialState
que pase a la función
createStore()
, pero si no pasó ninguno, o si el campo correspondiente no está configurado, se elige el argumento de
state
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, ya que puede usar
combineReducers()
en muchos niveles, o incluso componer reductores manualmente llamando a reductores y dándoles la parte relevante del árbol de estado.
El estado inicial en una aplicación Redux se puede configurar de dos maneras:
-
createStore
como el segundo argumento paracreateStore
( enlace de documentación ) - pásalo como el primer argumento para tus (sub) reductores ( enlace de documentación )
Si pasa el estado inicial a su tienda, ¿cómo lee ese estado de la tienda y lo convierte en el primer argumento en sus reductores?
¿Cómo lee ese estado de la tienda y lo convierte en el primer argumento en sus reductores?
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)
}
En pocas palabras: es Redux quien pasa el estado inicial a los reductores, no necesita hacer nada.
Cuando llama a
createStore(reducer, [initialState])
le está
createStore(reducer, [initialState])
Redux cuál es el estado inicial que se debe pasar al reductor cuando entra la primera acción.
La segunda opción que menciona, se aplica solo en caso de que no haya pasado un estado inicial al crear la tienda. es decir
function todoApp(state = initialState, action)
el estado solo se inicializará si Redux no pasó ningún estado
Espero que esto responda a su solicitud (que entendí como inicialización de reductores al pasar intialState y devolver ese estado)
Así es como lo hacemos (advertencia: copiado del código mecanografiado).
La esencia de esto es la prueba
if(!state)
State) en la función mainReducer (factory)
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 ) {
....
}
}
}