learning Redux from todo


## 目录

接下来要设计到以下的内容

- Actions & action creator
- Reducers
- Store
- Data Flow
- Usage with React
- Example: Todo List


### 动作及动作创建者

所谓动作就是普通 javascript 的对象, 它用来描述动作的内容, 
他们从 app 发送到 store. 他们就是 store 里的数据的源头. 通过` store.dispatch()` 来发送数据.

```
{
  type: 'ADD_TODO',
  text: 'Build my first Redux app'

}
```
> 通常我们把动作的类型定义为一个常量


```
const ADD_TODO = 'ADD_TODO'
```


其实所谓 **动作** 就是一个普通的 **javascript** 对象, 它必须有一个` type` 属性, 用来表明它能做什么样的动作.而这个 type 必须是一个声明为常量的字符串对象. 而且一般app 的 action 都会很多,所以可以单独把他们放在一个文件. 然后可以这样引用:

```
import { ADD_TODO, REMOVE_TODO } from '../actionTypes'
```

接下来,我们将要创建一个新的动作,用来描述用户勾选某个 todo 为完成的动作. 我们会通过` index` 来标记每一个` todo` .因为我们把他们放在` array` 里,这里只是 demo, 而真实的 app 最好有专门的逻辑用来生成找个 id.

```
{
  type: TOGGLE_TODO,
  index: 5
}
```

在这,其实每一个动作尽可能让它携带最简化的数据集. 例如, 我们通过` index` 就可以代表这个 todo对象.

```
{
  type: SET_VISIBILITY_FILTER,
  filter: SHOW_COMPLETED
}
```

`动作`我们先说到这儿,接下来说说`动作创建者`. 在 Redux 中, **动作的创建者** 就是一个地道的函数, 用来创建并返回一个动作. 可能很容易在这里混淆`动作`和`动作创建者`.

`动作创建者`的作用就是返回一个动作:

```
function addTodo(text){
  return {
    type: ADD_TODO,
    text
  }
}
```

---

> 在传统的 Flux 架构中, 一个动作者通常在定义一个 action, 之后还有`分发`到已经注册的某个 store 里?

```
function addTodoWithDispatch(text) {
  const action = {
    type: ADD_TODO,
    text
  }
  dispatch(action)
}

```


---

而在 Redux 中不这么处理. 取而代之的是去调用` Store` 的` dispatch()`函数. 把一个动作作为参数传递给它.

```
Store.dispatch(addTodo(text))
Store.dispatch(completeTodo(index))
```


或者,可以创建一个`绑定动作创建者( bound action creator)` 来自动实现动作的分发:

```
const boundAddTodo = (text) => dispatch(addTodo(text))
const boundCompleteTodo = (index) => dispatch(completeTodo(index))

```

现在可以这样使用他们:

```
boundAddTodo(text)
boundCompleteTodo(index)
```

这个分发` dispatch()` 函数可以直接通过` store.dispatch()`在` store` 里访问到, 或者直接通过` react-redux` 的`帮助类方法 connect()`, 使用` bindActionCreators()`来**自动**将这些`动作创建者`绑定到对应的` dispatch()` 函数上.

动作创建者还可以是异步的,但具有副作用.

> You can read about async actions in the advanced tutorial to learn how to handle AJAX responses and compose action creators into async control flow. 

说了这么多, 来看看全部的源码.

```
/*
 * action types
 */

export const ADD_TODO = 'ADD_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'

/*
 * other constants
 */

export const VisibilityFilters = {
  SHOW_ALL: 'SHOW_ALL',
  SHOW_COMPLETED: 'SHOW_COMPLETED',
  SHOW_ACTIVE: 'SHOW_ACTIVE'
}

/*
 * action creators
 */

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 }
}
```


接着说, 刚才我们说到了`创建动作`和`动作创建者`以及如何绑定和分发动作.

接下来我们看看` reducer`.

### Reducer

现在我们考虑的问题就是, 动作分发了之后呢, 到哪儿去,干了什么!?

关于`动作`, 它是实实在在定义了干什么. 但是它干什么怎么才能够影响到 app 的状态呢, 让 app 做出对应的响应呢, **这就是` reducer` 的要做的.**

####设计状态结构

先来说说` state`,在 redux 里所有的状态都是保持在一个单个的对象里,现在我们来想想一下她的样子.

拿` todo app `来说,我们要存储2样东西:

- todo 的 list
	- 这个是最主要的,时刻都能够取到完整的 todo 数据.
- 当前被选中的显示过滤器(要显示什么状态的 todo)
	- 这里我们只需要保存当前用户选中需要显式那个类型的 todo 就好,(如当前进行的或者已经完成的.)

可能对于一个 app 来说,我们通常下意识会认为有很多事情,数据需要处理. 有很多的 case. 但是对于这里所讲的`状态`来说, 我们需要抽象,取出最主要的东西. 

比如说, 我们只需要 care 这些数据就可以, 利用这些数据我们就能够正常运转 app


```
{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true,
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}
```

> 在一个较复杂的 app 中,可能实体之间会相互引用.我们建议尽量把状态保持越简单越好. 不要有嵌套的关系存在. 
> 尽量然后每一个对象有一个 id 能够识别. 通过 id 来引用.想象 app 的状态就和 database 里的状态类似. 这个可以查看 [normalizr](https://github.com/gaearon/normalizr) 的详细文档来看.

#### 处理动作

上面我们大概知道了找个 todo app 里`状态`对象样子, 接下来我们写` reducer`. 

> 重要事情说三遍

**`reducer` 就是一个`纯函数`,接受2个参数, 第一个的当前的状态,第二个是动作, 然后返回最新的状态.**
**`reducer` 就是一个`纯函数`,接受2个参数, 第一个的当前的状态,第二个是动作, 然后返回最新的状态.**
**`reducer` 就是一个`纯函数`,接受2个参数, 第一个的当前的状态,第二个是动作, 然后返回最新的状态.**



```
(previousState, action) => newState
```

这个我就称之为` reducer`, 因为它其实是这样一个实现:

```
Array.prototype.reduce(reducer, ?initialValue)
```

对于` reducer` 来说保证它的[`纯洁性`](https://en.wikipedia.org/wiki/Pure_function)是非常重要的.在 reducer 里保证不能够发生如下的事情:

- 改变参数
- 不能够做其他事情如调用 API 或者修改 route
- 调用不 `pure` 的函数如 `Date.now()` 或者 `Math.random()`

这里先不讨论副作用的东西之后高阶时候会说. 现在我们只记住对于 reducer 的 function 必须是 pure 的. `给一样的参数,总是返回同样的结果,没有副作用,没有 api 调用,没有突变,只是一个计算`.

接下来我们一步步的来理解之前定义的` action`. 首选我们初始化`状态`. redux 会调用一个 reducer 而当前是` undefined` 状态. 此时就是一个机会来初始化 app 的状态.

```

import { VisibilityFilters } from './actions'

const initialState = {
  visibilityFilter = VisibilityFilters.SHOW_ALL,
  todos: []
}

function todoAPP(state, action) {
  if (typeof state === 'undefined'){
    return initialState
  }
  // For now, don't handle any actions
  // and just return the state given to us
  return state
}
```

上面代码里的关于` state` 可以用` es6`的`参数默认值`语法来写就是:

```
function todoAPP(state = initialState, action){
 return state
}
```

让我们来处理` SET_VISIBILITY_FILTER`, 我们只需要来修改这个` visibilityFilter` 的值:

```
function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}
```

>注意

1. 我们这里不会修改` state`,而是通过` object.assign()` 函数来拷贝这个对象. 
	2. 而` Object.assgin(state, {visibilityFilyer: action.filter})`同样是错误的. 因为` state` 作为第一个参数传递, 它将要被修改. 你必须传递一个空的对象给第一个参数. 看[这里](http://redux.js.org/docs/recipes/UsingObjectSpreadOperator.html)
2. 我们同时把默认的初始状态作为默认返回值. 这个很重要一旦遇到未知的 action的情况下还是要返回状态.

注意:

> Object.assign()是 es6的一部分,目前还没有大部分实现支持.需要一个 polyfill 来支持.我们用 Babel 的一个插件.

#### 处理更多动作

```
function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      })    
    default:
      return state
  }
}
```

上面很明显我们都不是直接在修改` state`, 而是取而代之用一个新的 object 来接住之前的 state, 然后再 merge 最后的 todo, 这样做到不修改原有的状态.

最后` TOGGLE_TODO` 的动作也就很容易了:


```
case TOGGLE_TODO:
  return Object.assign({}, state, {
    todos: state.todos.map((todo, index) => {
      if (index === action.index) {
        return Object.assign({}, todo, {
          completed: !todo.completed
        })
      }
      return todo
    })
  })
```

我们希望要修改一个array 里特定的 ite m 而不突变.我们需要利用当前的 array创建一个新的 array, 而除了那个即将要被修改的. 而这样的动作似乎经常发生. 我们就可以接住帮助类如` react--addons-update`,`updeep` 来完成. 甚至有一个库可以干这样的事情, [Immutable(http://facebook.github.io/immutable-js/), 记住别去动 state.

### 分隔Reducers

```
function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      })
    case TOGGLE_TODO:
      return Object.assign({}, state, {
        todos: state.todos.map((todo, index) => {
          if(index === action.index) {
            return Object.assign({}, todo, {
              completed: !todo.completed
            })
          }
          return todo
        })
      })
    default:
      return state
  }
}

```

目前看上去我们的代码类似这样的. 看上去` todos` 和` visibilityFilter` 他们是单独更新的.有时候` state` 是需要更加的独立的.但是这里可以吧 todo 的更新先单独到一个函数里:

```
function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case TOGGLE_TODO:
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: !todo.completed
          })
        }
        return todo
      })
    default:
      return state
  }
}

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
    case TOGGLE_TODO:
      return Object.assign({}, state, {
        todos: todos(state.todos, action)
      })
    default:
      return state
  }
}
```


注意, 这里的` todos` 定义也是接收一个` state`, 而它是一个 array. 现在` todoAPP` 给了它一段 state 让他来管理(也就是todo list). 而 `todos` **知道如何来更新这个片段**(怎么知道的?). **这个称作 复合reducer,而且它是构建redux app 的基础模式**.

让我们来看看这个**复合的 reducer**, 我们是否可以把对于 visibilityFilter 的管理也抽出来作为 reducer manager 呢, 可以的:


```

function visibilityFilter(state = SHOW_ALL, action){
  switch (action.type){
    case SET_VISIBILITY_FILTER:
      return action.filer
      
    default:
      reuturn state
  }
}
```

现在我们可以重写主 reducer 来调用**每一个单独的reducer manager**. 然后把他们合并到一个单个的对象里. 从此它不需要再去知道完整的初始状态了. 只需要让子 reducer 来返回一个` undefined` 的初始状态.


```
function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case TOGGLE_TODO:
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: !todo.completed
          })
        }
        return todo
      })
    default:
      return state
  }
}

function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter
    default:
      return state
  }
}

function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  }
}
```

**注意**

> 这里每一个 reducer 他们手里面的` state` 并不是一样的,他们只负责他们各自对应的 **state片段**.

到这里看上去就不错了. 当随着 app 不断复杂,我们可以把 reducer 分割到不同的文件里,最后在主 reducer 里组合就好. 而且 redux 提供一个工具 -- `combineReducers()` 通过这个可以更加方便的把零散的 reducer 组合起来,所以我们可以重构之前的 todoAPP 如下:

```
import { combineReducers } from 'redux'

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp
```

还有这样的写法:

```
export default function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  }
}
```

甚至可以给每一个 reducer 指派不同的`键`, 然后这样用:


```
const reducer = combineReducers({
  a: doSomethingWithA,
  b: processB,
  c: c
})
function reducer(state = {}, action) {
  return {
    a: doSomethingWithA(state.a, action),
    b: processB(state.b, action),
    c: c(state.c, action)
  }
}
```


所有的` combineReducers()` 其实就做一件事情,生成一个函数,然后让每一个 reducer 来通过不同对应的键利用自己的一段 state.最后把结果合并起来赛道一个单个对象.

都能写成这样:

>Note for ES6 Savvy Users

>Because combineReducers expects an object, we can put all top-level reducers into a separate file, export each reducer function, and use import * as reducers to get them as an object with their names as the keys:


```
import { combineReducers } from 'redux'
import * as reducers from './reducers'

const todoApp = combineReducers(reducers)
```

>Because import * is still new syntax, we don't use it anymore in the documentation to avoid confusion, but you may encounter it in some community examples.


最终的代码如下:

```
import { combineReducers } from 'redux'
import { ADD_TODO, TOGGLE_TODO, SET_VISIBILITY_FILTER, VisibilityFilters } from './actions'
const { SHOW_ALL } = VisibilityFilters

function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter
    default:
      return state
  }
}

function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case TOGGLE_TODO:
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: !todo.completed
          })
        }
        return todo
      })
    default:
      return state
  }
}

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp

```

接下来我们看如何创建一个 redux 的 `store`, 它是如何处理` state` 还有如何 在指派一个 action 之后如何调用 reducer.



###Store

上面我们接触到如何定义 action 来表示发生了什么变化, 还有 **reducer** 它是如何通过不同的 action 来修改 app 的 state. 

下面我们要说的` Store` , **Store** 是什么,从字面意思看它是用来存储什么的, 对, 它就是存储 `state tree` 的. 

`Store` 有这样的作用:

- 管理app的 `state`
- 可以通过` getState()`来访问 `state`
- 通过` dispatch(action)`来修改 app 的` state`
- 通过` subscribe(listener)`来注册监听
- 处理没有注册的监听

---

再次重申: **Redux app** 的 `state` 是保持在一个单独的对象里. 当需要分割处理数据的逻辑时, 应该使用`复合 reducer`来替代多个 store.

---

如果你有 reducer,很容易来创建` store`. 上面我们使用了`combineReducer()`来组合多个 reducer, 现在我们引用它然后把它传递给` createStore()`.

```
import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)
```

>你可以通过传递一个可选的参数给`createStore()`来给 **app** 一个初始的状态,用来处理客户端和服务器端不同的状态(×)


```
let store = createStore(todoApp, window.STATE_FROM_SERVER)
```

###分发动作


至此, 我们已经创建了` Store`, 让我们来验证 app 是否工作,我们可以在没**UI** 的情况下测试更新逻辑:

import { addTodo, toggleTodo, setVisibilityFilter, VisibilityFilters } from './actions'

// Log the initial state
console.log(store.getState())

// Every time the state changes, log it
// Note that subscribe() returns a function for unregistering the listener
let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
)

// Dispatch some actions
// 通过 store.dispatch() 方法可以模拟用户交互, 出发 action
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))
store.dispatch(addTodo('Learn about store'))
store.dispatch(toggleTodo(0))
store.dispatch(toggleTodo(1))
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))

// Stop listening to state updates
unsubscribe()
```

![](http://i.imgur.com/zMMtoMz.png)

> index.js

```
import { createStore } from 'redux'
import todoApp from './reducers'

let store = createStore(todoApp)
```

**接下来我们要了解 redux app 里的数据流**


刚才说到了 [Store ](http://qiita.com/xiangzhuyuan/items/9ce23a8c9f3b26c48838)

接下来说 **数据流**

Redux 使用**单向数据流**

这就意味着在 app 里所有的数据都会经过同样 的生命周期模式. 让你的程序逻辑更加可以预测和容易理解.对于数据的处理同样要优化精华. 而不是让你死在多份同样的数据, 你手里的数据同样能够被他人来修改. 都不知道自己怎么死的.

如果还是没有被说服,建议读一下 [1](http://redux.js.org/docs/introduction/Motivation.html) [2](https://medium.com/@dan_abramov/the-case-for-flux-379b7d1982c6) 同时建议读一下[ Redux 不简单是 Flux 的变种](http://redux.js.org/docs/introduction/PriorArt.html)它同样说明了一些为什么要单向数据流的好处.

Redux 应用里的数据大致会走4步:

1. 当你调用` store.dispatch(action). 触发了一个动作.

一个动作就是一个简单的对象,说明了会发生什么.

```

 { type: 'LIKE_ARTICLE', articleId: 42 }
 { type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } }
 { type: 'ADD_TODO', text: 'Read the Redux docs.' }
```

描述了一个动作, 已经一些参数用来指明动作作用的对象.
好比这样说: 一个动作就是'某某喜欢找个文章', 看 redux 的文章`等等.

**你可以在 redux app 的任何地方使用` store.dispatch(action), 包括组件或者 XHR 的回调, 甚至是一个循环调用.**

2. Redux store 调用你给它的 reducer 函数

**store 是会传递两个参数给` reducer` 的. 第一个是当前的状态, 第二个是 action, 然后返回 action 作用之后的最新状态.** 再来看个例子:

```
 let previousState = { // 当前的状态
   visibleTodoFilter: 'SHOW_ALL',  //要显示什么类型
   todos: [  //所有的 todo
     {
       text: 'Read the docs.',
       complete: false
     }
   ]
 }

 // 这是一个添加 todo 的动作
 let action = {
   type: 'ADD_TODO',
   text: 'Understand the flow.'
 }

 // 获取最新的状态
 let nextState = todoApp(previousState, action)


```

一定要保证` reducer` 都是 **pure** 的函数,也就是说每次给同样的参数返回结果都是一样的.
 
 3. **根 reducer** 可以组合多个 reducer 到一个单独的状态树里.

如何组织*根 reducer *完全是个人喜好, Redux 提供一个` combineReducers()` 函数用来帮助组织分散的 Reducer. 下面再看看他们是如何被组织起来的:


```
 function todos(state = [], action) {
   // Somehow calculate it...
   return nextState
 }

 function visibleTodoFilter(state = 'SHOW_ALL', action) {
   // Somehow calculate it...
   return nextState
 }

 let todoApp = combineReducers({
   todos,
   visibleTodoFilter
 })
```

当你触发一个动作, 然后 todoAPP 就会调用由`combineReducers`返回的对应的 reducer:


```
let nextTodos = todos(state.todos, action)
let nextVisibleTodoFilter = visibleTodoFilter(state.visibleTodoFilter, action)
```

其实上面的情况就是返回

```
 return {
   todos: nextTodos,
   visibleTodoFilter: nextVisibleTodoFilter
 }

```

4. **Redux Store** 会把所有的 **reducer** 返回的 state 写会单独完整的 state 里.

 
至此,算是我们已经完成的 backend 逻辑, 接下来我们会学习如何结合` react` 完全 UI 部分.


### 使用 React

这里说明的就是其实 Redux 它完成的逻辑是不限于 react 来完成 UI 部分的. 你完全可以使用如`Angular` `Ember` 或者` jQuery` 等 frontend 框架来写. 而只是 Redux 可以更好地和 react 或者 deku 结合使用. 因为从 react 他们的概念定义来说, 描述 UI 的过程就是在说明一个状态的函数. 和 Redux 响应一个动作出发状态更新. 

接下来我们使用 react 来构建这个 todoapp 的 UI 部分.

#### 安装 react redux

[react 绑定](https://github.com/reactjs/react-redux) 是单独与 redux 存在,我们现在安装它:

```
npm install --save react-redux
```



> If you don't use npm, you may grab the latest UMD build from unpkg (either a development or a production build). 
> The UMD build exports a global called window.
> ReactRedux if you add it to your page via a \<script\> tag.


### 展示容器组件(用来画 UI, 无逻辑)

**React redux 绑定**封装了`展示和容器组件分离`的概念. [若不熟悉先读它](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0),

很重要,可以离开放下这里,等你.


回来...

我们总结下有什么不同之处:

| \   |展示组件|容器组件|
|:--:|:-----:|:----:|
|目的|看上去像什么|他们是怎么工作的|
|和 Redux通信|不|是|
|怎么读取数据|从属性 props|订阅 redux 的状态|
|怎么修改数据|通过属性里的回调|触发 redux 的动作|
|谁写的|自己用手|通常由react redux 会生成|


我们需要处理的大部分的都是展示组件, 当然也要处理一些容器组件, 他们用来和` reudx store` 连接起来.

技术上来说, 你可以通过调用` store.subscribe()`来写容器组件,但是不建议你那样做.
因为` React redux` 通常会做很多的优化, 他们都是手动无法完成的. 
因为这个, 我们更加趋向于通过使用 react-reudx 的` connect()` 函数来生成他们.

###设计组件体系结构

之前我们同样遇到一个环节就是在[动手之前设计我们的 state 体系](http://redux.js.org/docs/basics/Reducers.html). 现在我们要设计如何组织 UI 结构了. 这里就不是 redux 的范畴了. 可以通过[如何使用 react](https://facebook.github.io/react/docs/thinking-in-react.html) 来学习理解如何处理.

我们在这里的设计很简单了, 我们要显示一个 todo 的 list. 点击 todo 的 item, 它就标记为完成. 而且可以过滤显示没有完成的,或者已经完成的.

---

####展示组件

通过上面大致的讲述,我现在脑海里就是这样一些东西浮现;

- TodoList 用来表示可见的 todo 列表
	- todos:Array 就是一组 todo 的 item, 大概内容就是 {id,text,completed}.
	- onTodoClick(id:number) 是一个回调当一个 todo 的 item 被点击的时候.

- Todo 就是表示一个单独的 todo
	- text:string 当前 todo 的内容
	- completed:boolean 当前是否标记为完成
	- onClick() 是一个回调, 当在 item 被点击的时候触发

- Link 回调用
	- onClick() link 在被点击后触发的回调

- Footer 用来显示什么状态的 toggle
- App 就是当前 app 的根元素

他们完全不依赖redux, 当你把 redux 迁移到其他的框架上时, 这些东西完全不需要修改. 他们只是用来渲染你想要在 UI 上看到的东西

---

#### 容器组件

容器组件在这里充当的角色就是用它来连接展示组件和 redux, 让redux 的数能对应的显示在展示组件上. 比如刚才定义的展示组件` TodoList` 组件它就需要一个容器比如叫` VisibleTodoList`, 然后让它订阅到` Redux store`里的当前对应可见的` todo`, 为了修改可见过滤器,我们需要一个比如` FilterLink` 的容器来完成渲染一个`Link` 来让它完成一个动作的触发当点击它的时候:

- VisibleTodoList 通过当前的显示过滤条件拿到 todo list
- FilterLink 获取当前的过滤条件然后渲染一个 link
	- Filter:string 


####其他组件

有时候很难界定它到底是展示组件还是容器组件. 例如有时候一个表单或者一个函数他们精密的缠绕,如

- AddTodo 就是一个输入框加一个`添加`按钮

技术上讲我们可以把他们分开,但是呢现在这个初级阶段我们最好把它混合在一起. 因为组件的逻辑也不是很麻烦, 随着 app 的成长,如何分割他们就变得越来越明显.


### 动手实现组件

我们先从`展示组件`开始,因为我们不需要考虑如何和 redux 联系起来.

####展示组件

这些都是普通的 React 组件.我们也不会涉及太多,就是写一些无状态的纯函数.

除非我们还需要一些本地的状态或者生命周期的方法.当然也不是说组件必须是函数.只是用函数简单.

如何你之后设计到本地状态,生命周期的方法,或者性能优化的时候可以把它们写成`类`.

 
```components/Todo.js

import React, { PropTypes } from 'react'
// 定义单个 todo 对象
// 需要3个参数,1 click事件, 2 是否完成, 3 显式文本
const Todo = ({ onClick, completed, text }) => (
  <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none' }} > {text} </li>
)

// 验证3个参数
Todo.propTypes = {
  onClick: PropTypes.func.isRequired,
  completed: PropTypes.bool.isRequired,
  text: PropTypes.string.isRequired
}

export default Todo
```




```components/TodoList.js
import React, { PropTypes } from 'react'
import Todo from './Todo'

const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

TodoList.propTypes = {
  todos: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired,
    completed: PropTypes.bool.isRequired,
    text: PropTypes.string.isRequired
  }).isRequired).isRequired,
  onTodoClick: PropTypes.func.isRequired
}

export default TodoList
```



```components/link.js
import React, { PropTypes } from 'react'

const Link = ({ active, children, onClick }) => {
  if (active) {
    return <span>{children}</span>
  }

  return (
    <a href="#"
       onClick={e => {
         e.preventDefault()
         onClick()
      }}
    >
      {children}
    </a>
  )
}

Link.propTypes = {
  active: PropTypes.bool.isRequired,
  children: PropTypes.node.isRequired,
  onClick: PropTypes.func.isRequired
}

export default Link
```


```components/Footer.js
import React from 'react'
import FilterLink from '../containers/FilterLink'

const Footer = () => (
  <p>
    Show:
    {" "}
    <FilterLink filter="SHOW_ALL">
      All
    </FilterLink>
    {", "}
    <FilterLink filter="SHOW_ACTIVE">
      Active
    </FilterLink>
    {", "}
    <FilterLink filter="SHOW_COMPLETED">
      Completed
    </FilterLink>
  </p>
)

export default Footer
```



```components/App.js
import React from 'react'
import Footer from './Footer'
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'

const App = () => (
  <div>
    <AddTodo />
    <VisibleTodoList />
    <Footer />
  </div>
)

export default App
```

#### 容器组件

现在是时候把这些视图组件 hook 到 redux 里通过创建一些容器组件. 技术上来说, 一个容器组件就是一个 React 的组件( component), 他们通过使用` store.subscribe()`来读取` Redux` 状态树中的部分状态数据,然后给视图组件的` props` 对象,之后展示的过程和逻辑. 你可以手敲一个容器组件, 但是不建议这么做有点傻,建议你通过使用 react redux 的库提供的` connect()` 函数来生成这些容器组件.他们同时提供了很多优化来防止重复没用的渲染.

 为了使用` connect()`,你需要定义一个特殊的函数名字叫做` mapStateToProps`, 它的作用就是告诉当前的 app 如何把 redux 里的特定的状态片段对应到特定组件的` props` 属性上.举个例子, `VisibleTodoList` 需要计算` todos`, 把它传给` TodoList`. 所以我们需要定义一个函数,它能够过滤出` state.todos`, 通过` state.visibilityFilter`, 之后在` mapStateToProps` 里是这样的:
 
```
const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed)
  }
}

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}
```

除了读取 redux 的状态,容器组件还可以分发动作. 通常流行的写法是你可以定义一个` mapDispatchToProps()`函数.他可以接收一个`dispatch()` 方法,然后返回一个回调参数, 这个回调参数就是你想要 hook到视图组件中的. 例如, 我们想要把` VisibleTodoList` 注入一个` onTodoClick` 的属性到` TodoList` 的组件.而且这个` onTodoClick`还可以方法` TOGGLE_TODO` 的动作:

```
const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}
```

最终,我们通过调用` connect()` 传递2个参数, 创建了` VisibleTodoList`对象.

```
import { connect } from 'react-redux'

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList
```

这些都是 React redux 基本的 API, 但是还有很多方便好用的方法所以[建议你阅读它的文档](https://github.com/reactjs/react-redux),如果你 经常担心` mapStateToProps`创建新的对象,你可以学习[computing derived data](http://redux.js.org/docs/recipes/ComputingDerivedData.html) with [reselect](https://github.com/reactjs/reselect).


接下来是全部的容器组件的代码:


```containers/FilterLink.js

import { connect } from 'react-redux'
import { setVisibilityFilter } from '../actions'
import Link from '../components/Link'

const mapStateToProps = (state, ownProps) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    onClick: () => {
      dispatch(setVisibilityFilter(ownProps.filter))
    }
  }
}

const FilterLink = connect(
  mapStateToProps,
  mapDispatchToProps
)(Link)

export default FilterLink
```



```containers/VisibleTodoList.js
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed)
  }
}

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList
```

###其他的组件

 
```containers/AddTodo.js

import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'

let AddTodo = ({ dispatch }) => {
  let input

  return (
    <div>
      <form onSubmit={e => {
        e.preventDefault()
        if (!input.value.trim()) {
          return
        }
        dispatch(addTodo(input.value))
        input.value = ''
      }}>
        <input ref={node => {
          input = node
        }} />
        <button type="submit">
          Add Todo
        </button>
      </form>
    </div>
  )
}
AddTodo = connect()(AddTodo)

export default AddTodo
```

###传递 Store

所有的容器组件都能够访问 Redux 的 store, 所以他们可以订阅它. 有个方法就是可以把它当做一个参数 prop 传递给每一个容器组件. 但是这样很无聊,你需要把它在所有视图组件中传递?
就是因为他们在组件树里很深的位置哦.?**不懂**

我们推荐的一个方法就是使用一个特殊的 react redux 组件叫做` <Provider>`,它可以很魔性的让 `store `变得在所有的容器组件中可见, 而不需要再去显示的传递它, 因为你只需要在根组件里引用一次.



```index.js

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

```

到这里, 一个` todo` app 也就完事儿, 我们回过头来重新查看它的源码, 对比上面我们所讲解过的一切. 再去感受如何 build 一个redux-react app.

-THE END-