# Action
首先,讓我們來給 action 下個定義。
**Action** 是把數據從應用(譯者注:這里之所以不叫 view 是因為這些數據有可能是服務器響應,用戶輸入或其它非 view 的數據 )傳到 store 的有效載荷。它是 store 數據的**唯一**來源。一般來說你會通過 [`store.dispatch()`](../api/Store.md#dispatch) 將 action 傳到 store。
添加新 todo 任務的 action 是這樣的:
```js
const ADD_TODO = 'ADD_TODO'
```
```js
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
```
Action 本質上是 JavaScript 普通對象。我們約定,action 內必須使用一個字符串類型的 `type` 字段來表示將要執行的動作。多數情況下,`type` 會被定義成字符串常量。當應用規模越來越大時,建議使用單獨的模塊或文件來存放 action。
```js
import { ADD_TODO, REMOVE_TODO } from '../actionTypes'
```
> ##### 樣板文件使用提醒
> 使用單獨的模塊或文件來定義 action type 常量并不是必須的,甚至根本不需要定義。對于小應用來說,使用字符串做 action type 更方便些。不過,在大型應用中把它們顯式地定義成常量還是利大于弊的。參照 [減少樣板代碼](../recipes/ReducingBoilerplate.md) 獲取更多保持代碼簡潔的實踐經驗。
除了 `type` 字段外,action 對象的結構完全由你自己決定。參照 [Flux 標準 Action](https://github.com/acdlite/flux-standard-action) 獲取關于如何構造 action 的建議。
這時,我們還需要再添加一個 action index 來表示用戶完成任務的動作序列號。因為數據是存放在數組中的,所以我們通過下標 `index` 來引用特定的任務。而實際項目中一般會在新建數據的時候生成唯一的 ID 作為數據的引用標識。
```js
{
type: TOGGLE_TODO,
index: 5
}
```
**我們應該盡量減少在 action 中傳遞的數據**。比如上面的例子,傳遞 `index` 就比把整個任務對象傳過去要好。
最后,再添加一個 action type 來表示當前的任務展示選項。
```js
{
type: SET_VISIBILITY_FILTER,
filter: SHOW_COMPLETED
}
```
## Action 創建函數
**Action 創建函數** 就是生成 action 的方法。“action” 和 “action 創建函數” 這兩個概念很容易混在一起,使用時最好注意區分。
在 Redux 中的 action 創建函數只是簡單的返回一個 action:
```js
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
```
這樣做將使 action 創建函數更容易被移植和測試。
在 [傳統的 Flux](http://facebook.github.io/flux) 實現中,當調用 action 創建函數時,一般會觸發一個 dispatch,像這樣:
```js
function addTodoWithDispatch(text) {
const action = {
type: ADD_TODO,
text
}
dispatch(action)
}
```
不同的是,Redux 中只需把 action 創建函數的結果傳給 `dispatch()` 方法即可發起一次 dispatch 過程。
```js
dispatch(addTodo(text))
dispatch(completeTodo(index))
```
或者創建一個 **被綁定的 action 創建函數** 來自動 dispatch:
```js
const boundAddTodo = text => dispatch(addTodo(text))
const boundCompleteTodo = index => dispatch(completeTodo(index))
```
然后直接調用它們:
```
boundAddTodo(text);
boundCompleteTodo(index);
```
store 里能直接通過 [`store.dispatch()`](../api/Store.md#dispatch) 調用 `dispatch()` 方法,但是多數情況下你會使用 [react-redux](http://github.com/gaearon/react-redux) 提供的 `connect()` 幫助器來調用。[`bindActionCreators()`](../api/bindActionCreators.md) 可以自動把多個 action 創建函數 綁定到 `dispatch()` 方法上。
Action 創建函數也可以是異步非純函數。你可以通過閱讀 [高級教程](../advanced/README.md) 中的 [異步 action](../advanced/AsyncActions.md)章節,學習如何處理 AJAX 響應和如何把 action 創建函數組合進異步控制流。因為基礎教程中包含了閱讀高級教程和異步 action 章節所需要的一些重要基礎概念, 所以請在移步異步 action 之前, 務必先完成基礎教程。
## 源碼
### `actions.js`
```js
/*
* action 類型
*/
export const ADD_TODO = 'ADD_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'
/*
* 其它的常量
*/
export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE'
}
/*
* action 創建函數
*/
export function addTodo(text) {
return { type: ADD_TODO, text }
}
export function toggleTodo(index) {
return { type: TOGGLE_TODO, index }
}
export function setVisibilityFilter(filter) {
return { type: SET_VISIBILITY_FILTER, filter }
}
```
## 下一步
現在讓我們 [開發一些 reducers](Reducers.md) 來說明在發起 action 后 state 應該如何更新。
- 自述
- 介紹
- 動機
- 核心概念
- 三大原則
- 先前技術
- 學習資源
- 生態系統
- 示例
- 基礎
- 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
- 排錯