# Reducer 和 State 的基本結構
## Reducer 的基本結構
首先必須明確的是,整個應用只有一個**單一的 reducer 函數**:這個函數是傳給 `createStore` 的第一個參數。一個單一的 reducer 最終需要做以下幾件事:
- reducer 第一次被調用的時候,`state` 的值是 `undefined`。reducer 需要在 action 傳入之前提供一個默認的 state 來處理這種情況。
- reducer 需要先前的 state 和 dispatch 的 action 來決定需要做什么事。
- 假設需要更改數據,應該用更新后的數據創建新的對象或數組并返回它們。
- 如果沒有什么更改,應該返回當前存在的 state 本身。
寫 reducer 最簡單的方式是把所有的邏輯放在一個單獨的函數聲明中,就像這樣:
```javascript
function counter(state, action) {
if (typeof state === 'undefined') {
state = 0 // 如果 state 是 undefined,用這個默認值初始化 store
}
if (action.type === 'INCREMENT') {
return state + 1
} else if (action.type === 'DECREMENT') {
return state - 1
} else {
return state // 未識別 action 會經過這里
}
}
```
這個簡單的函數滿足上面提到的所有基本要求。在最開始會返回一個默認的值初始化 store;根據 action 的 type 決定 state 是哪種類型的更新,最后返回新的 state;如果沒有什么要發生,會返回先前的 state。
這里有一些對這個 reducer 的簡單調整。首先,重復的 `if/else` 語句看上去是很煩人的,可以使用 `switch` 語句代替他。其次,我們可以使用 ES6 的默認參數來處理初始 state 不存在的情況。有了這些變化,reducer 看上去會長成這樣:
```javascript
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
```
這是典型 Redux reducer 的基本結構。
## State 的基本結構
Redux 鼓勵你根據要管理的數據來思考你的應用程序。數據就是你應用的 state,state 的結構和組織方式通常會稱為 "shape"。在你組織 reducer 的邏輯時,state 的 shape 通常扮演一個重要的角色。
Redux state 中頂層的狀態樹通常是一個普通的 JavaScript 對象(當然也可以是其他類型的數據,比如:數字、數據或者其他專門的數據結構,但大多數庫的頂層值都是一個普通對象)。在頂層對象中組織數據最常見的方式是將數據劃分為子樹,每個頂層的 key 對應著和特定域或者切片相關聯的數據。例如,Todo 應用的 state 通常長這樣:
```javascript
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
```
在這個例子中,`todos` 和 `visibilityFilter` 都是 state 的頂層 Key,他們分別代表著一個某個特定概念的數據切片。
大多數應用會處理多種數據類型,通常可以分為以下三類:
- 域數據(Domain data): 應用需要展示、使用或者修改的數據(比如 從服務器檢索到的所有 todos
- 應用狀態(App state): 特定于應用某個行為的數據(比如 “Todo #5 是現在選擇的狀態”,或者 “正在進行一個獲取 Todos 的請求”)
- UI 狀態(UI state): 控制 UI 如何展示的數據(比如 “編寫 TODO 模型的彈窗現在是展開的”)
Store 代表著應用核心,因此應該用域數據(Domain data)和應用狀態數據(App state)定義 State,而不是用 UI 狀態(UI state)。舉個例子,`state.leftPane.todoList.todos` 這樣的結構就是一個壞主意,因為整個應用的核心是 “todos” 而不僅僅是 UI 的一個模塊。 `todos` 這個切片才應該是 state 結構的頂層。
UI 樹和狀態樹之間很少有 1 對 1 的關系。除非你想明確的跟蹤你的 Redux Store 中存儲的 UI 數據的各個方面,但即使是這樣,UI 數據的結構和域數據的結構也是不一樣的。
一個典型的應用 state 大致會長這樣:
```javascript
{
domainData1 : {},
domainData2 : {},
appState1 : {},
appState2 : {},
ui : {
uiState1 : {},
uiState2 : {},
}
}
```
- 自述
- 介紹
- 動機
- 核心概念
- 三大原則
- 先前技術
- 學習資源
- 生態系統
- 示例
- 基礎
- 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
- 排錯