TL; DR
Sem combineReducers()
ou código manual semelhante, initialState
sempre vence state = ...
no redutor porque o state
passado para o redutor é initialState
e não é undefined
, portanto, a sintaxe do argumento ES6 não é aplicada neste caso.
Com combineReducers()
o comportamento é mais matizado. Os redutores cujo estado é especificado em initialState
receberão isso state
. Outros redutores receberão undefined
e, por causa disso , voltarão ao state = ...
argumento padrão que especificarem.
Em geral, initialState
vence o estado especificado pelo redutor. Isso permite que os redutores especifiquem dados iniciais que façam sentido para eles como argumentos padrão, mas também permite carregar os dados existentes (total ou parcialmente) quando você está hidratando o armazenamento a partir de algum armazenamento persistente ou do servidor.
Primeiro, vamos considerar um caso em que você tem um único redutor.
Diga que você não usa combineReducers()
.
Então, seu redutor pode ter a seguinte aparência:
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT': return state + 1;
case 'DECREMENT': return state - 1;
default: return state;
}
}
Agora, digamos que você crie uma loja com ele.
import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState());
O estado inicial é zero. Por quê? Porque o segundo argumento createStore
era undefined
. Este é state
passado ao seu redutor pela primeira vez. Quando o Redux é inicializado, ele despacha uma ação “fictícia” para preencher o estado. Portanto, seu counter
redutor foi chamado com state
igual a undefined
. Este é exatamente o caso que “ativa” o argumento padrão. Portanto, state
agora está de 0
acordo com o state
valor padrão ( state = 0
). Este estado ( 0
) será retornado.
Vamos considerar um cenário diferente:
import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState());
Por que é 42
, e não 0
, desta vez? Porque createStore
foi chamado com 42
o segundo argumento. Este argumento passa a ser state
passado ao seu redutor junto com a ação fictícia. Desta vez, state
não é indefinido (é 42
!), Então a sintaxe de argumento padrão do ES6 não tem efeito. O state
é 42
e 42
é retornado do redutor.
Agora vamos considerar um caso em que você usa combineReducers()
.
Você tem dois redutores:
function a(state = 'lol', action) {
return state;
}
function b(state = 'wat', action) {
return state;
}
O redutor gerado por se combineReducers({ a, b })
parece com este:
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
};
}
Se chamarmos createStore
sem o initialState
, ele inicializará o state
para {}
. Portanto, state.a
e state.b
será undefined
quando ele ligar a
e b
redutores. Ambos a
e os b
redutores receberão undefined
como seus state
argumentos e, se especificarem state
valores padrão , eles serão retornados. É assim que o redutor combinado retorna um { a: 'lol', b: 'wat' }
objeto de estado na primeira chamada.
import { createStore } from 'redux';
let store = createStore(combined);
console.log(store.getState());
Vamos considerar um cenário diferente:
import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState());
Agora eu especifiquei o initialState
como o argumento para createStore()
. O estado retornado do redutor combinado combina o estado inicial que eu especifiquei para o a
redutor com o 'wat'
argumento padrão especificado que o b
próprio redutor escolheu.
Vamos lembrar o que o redutor combinado faz:
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
};
}
Neste caso, state
foi especificado para não retornar {}
. Era um objeto com a
campo igual a 'horse'
, mas sem o b
campo. É por isso que o a
redutor recebeu 'horse'
como seu state
e alegremente o devolveu, mas o b
redutor recebeu undefined
como seu state
e, portanto, retornou sua ideia de default state
(em nosso exemplo, 'wat'
). É assim que recebemos { a: 'horse', b: 'wat' }
em troca.
Para resumir, se você seguir as convenções do Redux e retornar o estado inicial dos redutores quando eles forem chamados com undefined
o state
argumento (a maneira mais fácil de implementar isso é especificar o state
valor do argumento padrão ES6), você terá um bom comportamento útil para redutores combinados. Eles irão preferir o valor correspondente no initialState
objeto que você passar para a createStore()
função, mas se você não passou nenhum, ou se o campo correspondente não foi definido, o padrão.state
argumento especificado pelo redutor é escolhido. Esta abordagem funciona bem porque fornece inicialização e hidratação dos dados existentes, mas permite que redutores individuais redefinam seu estado se seus dados não foram preservados. Claro, você pode aplicar este padrão recursivamente, pois você pode usarcombineReducers()
em muitos níveis, ou mesmo componha redutores manualmente chamando redutores e dando a eles a parte relevante da árvore de estado.
combineReducers
. Muito obrigado novamente.