# 初始化 State
主要有兩種方法可以用來初始化你的應用的 state 。
1. 可以通過 `createStore` 方法,該方法接受可選的 `preloadedState` 做為其第二個參數;
2. reducer 中 state 參數的默認值為 `undefined` ,修改其默認值也可以用來初始化應用的 state,可以在 reducer 中檢查 state 值是否為 `undefined` 從而顯式的添加默認值,也可以通過 ES6 中默認參數的語法來添加默認值:
`function myReducer(state = someDefaultValue, action)`
兩種初始化 State 的方法會如何相互影響并沒有那么直觀,不過好在它們相互作用的過程遵守下面的這些明確的規則。
## 概要
如果沒有使用 `combineReducers()` 或者類似功能的代碼,`preloadedState` 優先級會比在 reducer 里面使用 `state = ...` 高 ,這是因為 `preloadedState` 會做為 `state` 傳遞到 reducer 中,`state` 的值不再是 `undefined` ,ES6 的默認參數不會生效。
如果使用了 `combineReducers()` 方法,結果就會有一些細微的差別。那些在 `preloadedState` 中指明了 `state` 的 reducer 會以對應傳入的值做為初始值。其他的 reducer 接收到的則還是 `undefined` 則還會使用 `state = ...` 指定的默認值。
**通常情況下,通過 `preloadedState` 指定的 state 優先級要高于通過 reducer 指定的 state。這種機制的存在允許我們在 reducer 可以通過指明默認參數來指定初始數據,而且還為通過服務端或者其它機制注入數據到 store 中提供了可能。**
> 注意:通過 `preloadedState` 傳入了初始化數據的 reducers 仍然需要添加默認值來應對傳遞的 state 為 `undefined` 的情況。這樣,當所有的 reducers 在初始化時被傳入 state 的都是 `undefined` 時,還可以返回一些默認值,默認值可以是任何非 `undefined` 的值,不過也沒有必要復制 `preloadedState` 做為其默認值。
## 深入理解
### 單個簡單的 Reducer
首先我們來考慮只有一個 reducer 的情況,這種情況下我們無須使用 `combineReducers()` 方法。
此時的 reducer 可能會是下面這個樣子:
```js
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
```
接下來,假設你通過上面的 reducer 創建了一個 store。
```js
import { createStore } from 'redux'
const store = createStore(counter)
console.log(store.getState()) // 0
```
結果我們得到的初始的 state 的值為 0 ,為什么呢?
因為你傳給 `createStore` 的第二個參數是 `undefined`,也就是說你傳給 reducer 中的 state 的初始值也是 `undefined` 。當 Redux 初始化時,實際上會觸發一個 “dummy” action 來填充 store。當 reducer `counter` 被調用時,由于傳入的 `state` 等于 `undefined` ,默認參數生效了。因此 state 的值為設定的默認值 0。
我們來看看另一種不同的場景:
```js
import { createStore } from 'redux'
const store = createStore(counter, 42)
console.log(store.getState()) // 42
```
此時得到的值為 `42`,不是 `0`,為什么又會這樣呢?
這種情況下 `createStore` 方法被調用時,第二個參數傳入的是 `42`。這個值會在 “dummmy” action 觸發時傳遞給 state,由于這個值不為 `undefined`,因此設定的默認值不會起作用,在 reducer 中 `state` 返回值 42。
### 合并的 Reducers
我們再來看看使用了 `combineReducers()` 方法的情況。
假設你有下面這兩個 reducer:
```js
function a(state = 'lol', action) {
return state;
}
?
function b(state = 'wat', action) {
return state;
}
```
通過 `combineReducers({ a, b })` 方式我們可以把上面兩個 reducer 合并為一個 reducer
```js
// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
}
}
```
如果我們調用 `createStore` 方法時,不傳入 `preloadedState` ,初始化 `state` 會為 `{}`。在調用對應的 reducer 時會傳入 `state.a` 和 `state.b` 做為 `state` 的參數,不過由于這兩個值都是 `undefined`。a 和 b 兩個 reducer 都會接收 `undefined` 作為 `state` 的參數,此時如果 `state` 有默認值,就會返回默認值。因此上述組合 reducer 在首次調用時會返回 `{ a: 'lol', b: 'wat' }`。
```js
import { createStore } from 'redux'
const store = createStore(combined)
console.log(store.getState()) // { a: 'lol', b: 'wat' }
```
我們再來看看一個不同的情況:
```js
import { createStore } from 'redux'
const store = createStore(combined, { a: 'horse' })
console.log(store.getState()) // { a: 'horse', b: 'wat' }
```
上述代碼中我在 `createStore()` 方法調用時傳入了 `preloadedState` 。得到的結果是我為 a reducer 指定的值和 b reducer 的默認值組合而成。
我們仔細來看看這個組合 reducer 做了什么:
```js
// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
}
}
```
上面的代碼中,指定了組合 reducer 的 `state` ,因此其值不會是默認的 `{}`, 而是一個對象,并且該對象中鍵 `a` 的值為 `’horse'`,但是并不存在鍵 `b`。這就導致 a reducer 接受 `horse` 作為其 state 的參數,而 b reducer 則接收 `undefined` 做為其 `state` 的參數,因此在 b reducer 中設置的默認 `state` 會生效 (在本例中是 `'wat'`)。最終我們得到的結果為 `{ a: 'horse', b: 'wat'}`。
## 總結
總結一下,如果你使用 redux 的推薦做法,在 reducer 中給定 state 參數的默認值(最簡單的方法是通過 ES6 的默認值語法),你將擁有一個表現良好的組合 reducer。**這個組合 reducer 會優先使用你通過 `preloadedState` 傳遞來的對應的值,不過就算你沒傳遞或者不存在對應的值,也會使用你設定的默認值。**這種機制非常棒,它提供了設置初始值并注入的途徑,也保留了 reducer 設置默認值的能力。加上`combineReducers()` 可以在不同級別上使用,這種模式可以遞歸的使用。
- 自述
- 介紹
- 動機
- 核心概念
- 三大原則
- 先前技術
- 學習資源
- 生態系統
- 示例
- 基礎
- Action
- Reducer
- Store
- 數據流
- 搭配 React
- 示例:Todo List
- 高級
- 異步 Action
- 異步數據流
- Middleware
- 搭配 React Router
- 示例:Reddit API
- 下一步
- 技巧
- 配置 Store
- 遷移到 Redux
- 使用對象展開運算符
- 減少樣板代碼
- 服務端渲染
- 編寫測試
- 計算衍生數據
- 實現撤銷重做
- 子應用隔離
- 組織 Reducer
- Reducer 基礎概念
- Reducer 基礎結構
- Reducer 邏輯拆分
- Reducer 重構示例
- combineReducers 用法
- combineReducers 進階
- State 范式化
- 管理范式化數據
- Reducer 邏輯復用
- 不可變更新模式
- 初始化 State
- 結合 Immutable.JS 使用 Redux
- 常見問題
- 綜合
- Reducer
- 組織 State
- 創建 Store
- Action
- 不可變數據
- 代碼結構
- 性能
- 設計哲學
- React Redux
- 其它
- 排錯
- 詞匯表
- API 文檔
- createStore
- Store
- combineReducers
- applyMiddleware
- bindActionCreators
- compose
- react-redux 文檔
- API
- 排錯