16、ReactJS - 使用 Flux

要将 ReduxReact 结合起来使用,就还需要一些额外的库,其中最重要的是 react-redux

react-redux 提供了两个重要的对象,Providerconnect,前者使 React 组件可被连接(connectable),后者把 React 组件和 Redux 的 store 真正连接起来

本章节将介绍如何实现 Flux模式。 我们使用 Redux 类库,然后一步一步的把 ReduxReact 组合在一起

第一步 - 安装 Redux

安装 Redux 的命令如下

npm install --save react-redux

第二步 - 生成项目目录和文件

创建目录结构和相应的文件  

第三步 - Actions

Action 是纯声明式的数据结构,只提供事件的所有要素,不提供逻辑。

actions/actions.js

export const ADD_TODO = 'ADD_TODO'

let nextTodoId = 0;

export function addTodo(text) {
   return {
      type: ADD_TODO,
      id: nextTodoId++,
      text
   };
}

第四步 - Reducers

reducer 是一个匹配函数,action的发送是全局的:所有的reducer都可以捕捉到并匹配与自己相关与否,相关就拿走action中的要素进行逻辑处理,修改store中的状态,不相关就不对state做处理原样返回

reducers 作为事件处理函数,它接收 stateaction 作为参数,进行一系列的计算,然后返回计算后的 state

在本范例中,我们定义了两个函数,todo 用来添加待办事项, todos 用来生成列表。

帮助函数 combineReducers 方便管理和添加 reducers .

reducers/reducers.js

import { combineReducers } from 'redux'
import { ADD_TODO } from '../actions/actions'

function todo(state, action) {
   switch (action.type) {

      case ADD_TODO:
         return {
            id: action.id,
            text: action.text,
         }

      default:
      return state
   }
}

function todos(state = [], action) {
   switch (action.type) {

      case ADD_TODO:
         return [
            ...state,
            todo(undefined, action)
         ]

      default:
      return state
   }
}

const todoApp = combineReducers({
   todos
})

export default todoApp

Step 5 - Store

Redux 的 store 用来管理 React 应用的所有状态(state)。 可以简单的使用 createStore 创建一个 store ,然后把它赋值给 providerstore属性就可以完成 storeprovider的绑定

main.js

import React from 'react'

import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'

import App from './App.jsx'
import todoApp from './reducers/reducers'

let store = createStore(todoApp)

let rootElement = document.getElementById('app')

render(

   <Provider store = {store}>
      <App />
   </Provider>,

   rootElement
)

第六步 6 - App Component

App 组件是应用程序的入口组件,只有 App 组件能被 redux 唤醒。 其中 connect 函数是最重要的部分,他把 App 组件连接到 Redux 的 store

connect 使用 select 作为参数,而 select 函数从 Redux 的 store 中提取 state 数据,然后注入 props ( visibleTodos ) 到我们的组件中.

App.js

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

import AddTodo from './components/AddTodo.jsx'
import TodoList from './components/TodoList.jsx'

class App extends Component {
   render() {
      const { dispatch, visibleTodos } = this.props

      return (
         <div>

            <AddTodo
               onAddClick = {text ⇒
               dispatch(addTodo(text))}
            />

            <TodoList todos = {visibleTodos}/>

         </div>
      )
   }
}

function select(state) {
   return {
      visibleTodos: state.todos
   }
}

export default connect(select)(App)

Step 7 - Other Components

下面这些组件不会被 Redux 唤醒 也就是说,下面这些组件和 redux 可以说没有任何关系。

components/AddTodo.js

import React, { Component, PropTypes } from 'react'

export default class AddTodo extends Component {
   render() {
      return (
         <div>
            <input type = 'text' ref = 'input' />

            <button onClick = {(e) ⇒ this.handleClick(e)}>
               Add
            </button>

         </div>
      )
   }

   handleClick(e) {
      const node = this.refs.input
      const text = node.value.trim()
      this.props.onAddClick(text)
      node.value = ''
   }
}

components/Todo.js

import React, { Component, PropTypes } from 'react'

export default class Todo extends Component {
   render() {
      return (
         <li>
            {this.props.text}
         </li>
      )
   }
}

components/TodoList.js

import React, { Component, PropTypes } from 'react'
import Todo from './Todo.jsx'

export default class TodoList extends Component {
   render() {
      return (
         <ul>
            {this.props.todos.map(todo ⇒
               <Todo
               key = {todo.id}
               {...todo}
               />
            )}
         </ul>
      )
   }
}

运行我们的 App,添加一个 待办事项 试一试吧