# Store
Store 就是用來維持應用所有的 [state 樹](../Glossary.md#state) 的一個對象。
改變 store 內 state 的惟一途徑是對它 dispatch 一個 [action](../Glossary.md#action)。
Store 不是類。它只是有幾個方法的對象。
要創建它,只需要把根部的 [reducing 函數](../Glossary.md#reducer) 傳遞給 [`createStore`](createStore.md)。
> ##### Flux 用戶使用注意
> 如果你以前使用 Flux,那么你只需要注意一個重要的區別。Redux 沒有 Dispatcher 且不支持多個 store。**相反,只有一個單一的 store 和一個根級的 reduce 函數[reducing function](../Glossary.md#reducer)**。隨著應用不斷變大,你應該把根級的 reducer 拆成多個小的 reducers,分別獨立地操作 state 樹的不同部分,而不是添加新的 stores。然后你可以使用 [`combineReducers`](combineReducers.md) 來連接他們。這就像一個 React 應用只有一個根級的組件,這個根組件又由很多小組件構成。
### Store 方法
- [`getState()`](#getState)
- [`dispatch(action)`](#dispatch)
- [`subscribe(listener)`](#subscribe)
- [`replaceReducer(nextReducer)`](#replaceReducer)
## Store 方法
### <a id='getState'></a>[`getState()`](#getState)
返回應用當前的 state 樹。
它與 store 的最后一個 reducer 返回值相同。
#### 返回值
_(any)_: 應用當前的 state 樹。
<hr>
### <a id='dispatch'></a>[`dispatch(action)`](#dispatch)
分發 action。這是觸發 state 變化的惟一途徑。
將使用當前 [`getState()`](#getState) 的結果和傳入的 `action` 以同步方式的調用 store 的 reduce 函數。它的返回值會被作為下一個 state。從現在開始,這就成為了 [`getState()`](#getState) 的返回值,同時變化監聽器(change listener)會被觸發。
> ##### Flux 用戶使用注意
>
> 當你在 [reducer](../Glossary.md#reducer) 內部調用 `dispatch` 時,將會拋出錯誤提示“Reducers may not dispatch actions.(Reducer 內不能 dispatch action)”。這就相當于 Flux 里的 “Cannot dispatch in a middle of dispatch(dispatch 過程中不能再 dispatch)”,但并不會引起對應的錯誤。在 Flux 里,當 Store 處理 action 和觸發 update 事件時,dispatch 是禁止的。這個限制并不好,因為他限制了不能在生命周期回調里 dispatch action,還有其它一些本來很正常的地方。
> 在 Redux 里,只會在根 reducer 返回新 state 結束后再會調用事件監聽器,因此,你可以在事件監聽器里再做 dispatch。惟一使你不能在 reducer 中途 dispatch 的原因是要確保 reducer 沒有副作用。如果 action 處理會產生副作用,正確的做法是使用異步 [action 創建函數](../Glossary.md#action-creator)。
#### 參數
1. `action` (_Object_<sup>?</sup>): 描述應用變化的普通對象。Action 是把數據傳入 store 的惟一途徑,所以任何數據,無論來自 UI 事件,網絡回調或者是其它資源如 WebSockets,最終都應該以 action 的形式被 dispatch。按照約定,action 具有 `type` 字段來表示它的類型。type 也可被定義為常量或者是從其它模塊引入。最好使用字符串,而不是 [Symbols](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol) 作為 action,因為字符串是可以被序列化的。除了 `type` 字段外,action 對象的結構完全取決于你。參照 [Flux 標準 Action](https://github.com/acdlite/flux-standard-action) 獲取如何組織 action 的建議。
#### 返回值
(Object<sup>?</sup>): 要 dispatch 的 action。
#### 注意
<sup>?</sup> 使用 [`createStore`](createStore.md) 創建的 “純正” store 只支持普通對象類型的 action,而且會立即傳到 reducer 來執行。
但是,如果你用 [`applyMiddleware`](applyMiddleware.md) 來套住 [`createStore`](createStore.md) 時,middleware 可以修改 action 的執行,并支持執行 dispatch [異步 actions](../Glossary.md#async-action)。異步 action 通常使用異步原語如 Promise、Observable 或者 Thunk。
Middleware 是由社區創建,并不會同 Redux 一起發行。你需要手動安裝 [redux-thunk](https://github.com/gaearon/redux-thunk) 或者 [redux-promise](https://github.com/acdlite/redux-promise) 庫。你也可以創建自己的 middleware。
想學習如何描述異步 API 調用?看一下 action 創建函數里當前的 state,執行一個有副作用的操作,或者以鏈式操作執行它們,參照 [`applyMiddleware`](applyMiddleware.md) 中的示例。
#### 示例
```js
import { createStore } from 'redux'
const store = createStore(todos, ['Use Redux'])
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}
store.dispatch(addTodo('Read the docs'))
store.dispatch(addTodo('Read about the middleware'))
```
<hr>
### <a id='subscribe'></a>[`subscribe(listener)`](#subscribe)
添加一個變化監聽器。每當 dispatch action 的時候就會執行,state 樹中的一部分可能已經變化。你可以在回調函數里調用 [`getState()`](#getState) 來拿到當前 state。
你可以在變化監聽器里面進行 [`dispatch()`](#dispatch),但你需要注意下面的事項:
1. 監聽器調用 [`dispatch()`](#dispatch) 僅僅應當發生在響應用戶的 actions 或者特殊的條件限制下(比如: 在 store 有一個特殊的字段時 dispatch action)。雖然沒有任何條件去調用 [`dispatch()`](#dispatch) 在技術上是可行的,但是隨著每次 [`dispatch()`](#dispatch) 改變 store 可能會導致陷入無窮的循環。
2. 訂閱器(subscriptions) 在每次 [`dispatch()`](#dispatch) 調用之前都會保存一份快照。當你在正在調用監聽器(listener)的時候訂閱(subscribe)或者去掉訂閱(unsubscribe),對當前的 [`dispatch()`](#dispatch) 不會有任何影響。但是對于下一次的 [`dispatch()`](#dispatch),無論嵌套與否,都會使用訂閱列表里最近的一次快照。
3. 訂閱器不應該注意到所有 state 的變化,在訂閱器被調用之前,往往由于嵌套的 [`dispatch()`](#dispatch) 導致 state 發生多次的改變。保證所有的監聽器都注冊在 [`dispatch()`](#dispatch) 啟動之前,這樣,在調用監聽器的時候就會傳入監聽器所存在時間里最新的一次 state。
這是一個底層 API。多數情況下,你不會直接使用它,會使用一些 React(或其它庫)的綁定。如果你想讓回調函數執行的時候使用當前的 state,你可以 [把 store 轉換成一個 Observable 或者寫一個定制的 `observeStore` 工具](https://github.com/rackt/redux/issues/303#issuecomment-125184409)。
如果需要解綁這個變化監聽器,執行 `subscribe` 返回的函數即可。
#### 參數
1. `listener` (_Function_): 每當 dispatch action 的時候都會執行的回調。state 樹中的一部分可能已經變化。你可以在回調函數里調用 [`getState()`](#getState) 來拿到當前 state。store 的 reducer 應該是純函數,因此你可能需要對 state 樹中的引用做深度比較來確定它的值是否有變化。
##### 返回值
(_Function_): 一個可以解綁變化監聽器的函數。
##### 示例
```js
function select(state) {
return state.some.deep.property
}
let currentValue
function handleChange() {
let previousValue = currentValue
currentValue = select(store.getState())
if (previousValue !== currentValue) {
console.log(
'Some deep nested property changed from',
previousValue,
'to',
currentValue
)
}
}
const unsubscribe = store.subscribe(handleChange)
unsubscribe()
```
<hr>
### <a id='replaceReducer'></a>[`replaceReducer(nextReducer)`](#replaceReducer)
替換 store 當前用來計算 state 的 reducer。
這是一個高級 API。只有在你需要實現代碼分隔,而且需要立即加載一些 reducer 的時候才可能會用到它。在實現 Redux 熱加載機制的時候也可能會用到。
#### 參數
1. `nextReducer` (_Function_) store 會使用的下一個 reducer。
- 自述
- 介紹
- 動機
- 核心概念
- 三大原則
- 先前技術
- 學習資源
- 生態系統
- 示例
- 基礎
- 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
- 排錯