React 和 Redux 快速開發實戰

圖片描述

今天聊一聊 react + redux 環境快速搭建,以及實戰一個 TodoList,多是有史以來最簡潔的方法哦,是否是很期待,當時橙子也是很吃驚這樣的搭建速度。html

本文適合有 react 和 redux 基礎的同窗看,固然你也能夠看完本文去學基礎。下面進入正題node

快速搭建環境

沒錯,看過前面博客的同窗猜到,我會選擇各類 cli 工具,固然你可能猜錯了,這裏用的不是 create-react-app 這個 star 最多的構建工具。react

這裏咱們用 react-redux-starter-kitwebpack

須要 node 4.5.0+ & npm 3.0.0+ 便可,首先git

git clone https://github.com/davezuko/react-redux-starter-kit.git <my-project-name>
cd <my-project-name>
npm install
npm start

沒錯你已經搭建環境完畢。好快的樣子!github

此時你能夠看到 localhost:3000 的首屏了,welcome 而後一個鴨子,奇怪的首屏,做者說 duck 是 redux 的諧音,好吧。。。web

此時可能你已經興奮的點完了,這個 cli 的所有內容。chrome

先不急看效果,既然是 redux,就要有對應的 chrome 插件,讓咱們直觀的看到 store 的變化。npm

推薦 redux-devtool,點開連接,添加至 chrome 便可。redux

此時開啓控制檯,找到 Redux 的 tab,會看到以下界面

圖片描述

功能強大,想深刻學習的能夠看文檔,這裏咱們只關心 Action 和 State 的變化

此時項目在 localhost:3000/counter 這個路由下有個累加器,Increment 每次同步加 1,Double(async) 每次異步乘 2。

異步處理速度慢?不是的看代碼會發現這樣一段

export const doubleAsync = () => {
  return (dispatch, getState) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        dispatch({
          type    : COUNTER_DOUBLE_ASYNC,
          payload : getState().counter
        })
        resolve()
      }, 200)
    })
  }
}

沒錯,是一個 200ms 的 setTimeout。

你們印象裏,官方文檔有說 action 是當即執行的同步函數,咱們能夠藉助中間件的形式實現異步,這裏用的是 redux-thunk,這種方式的確解決了問題,以後的文章還會單獨介紹更好的辦法。

代碼簡單過一遍,沒什麼難點。接下來咱們就能夠根據這個項目漸進開發了。

實戰 TodoList

好,咱們首先添加一個路由

src/compoments/Header/Header.js

在 div 標籤內最下面添加以下代碼

{' · '}
<Link to='/todo' activeClassName='route--active'>
  Todo
</Link>

src/routes/index.js

對應位置以下修改,添加一個子路由。

import CoreLayout from '../layouts/CoreLayout'
import Home from './Home'
import CounterRoute from './Counter'
import TodoRoute from './Todo'

export const createRoutes = (store) => ({
  path        : '/',
  component   : CoreLayout,
  indexRoute  : Home,
  childRoutes : [
    CounterRoute(store),
    TodoRoute(store)
  ]
})

咱們如今尚未 Todo 對應路由的組件,下面在 routes 下建立 Todo 文件夾,而後文件夾結構以下

Todo
  |-components
  |  |-Todo.js
  |
  |-containers
  |  |-TodoContainer.js
  |
  |-modules
  |  |-todo.js
  |
  |-index.js

簡單解釋一下這個目錄結構,index.js 是 Todo 做爲路由頁面的入口文件,components 下放的是模塊的視圖部分,containers 做爲一個容器來綁定組件的 event、 state 和 prop,modules 主要是負責從 action->reducer->newState 的過程。

條例清晰,梳理完結構,看看每一個文件都怎麼完成的

index.js

import { injectReducer } from '../../store/reducers'

export default (store) => ({
  path : 'todo',
  getComponent (nextState, cb) {
    require.ensure([], (require) => {
      const Todo = require('./containers/TodoContainer').default
      const { itemReducer } = require('./modules/todo')
      const { todoListReducer } = require('./modules/todo')

      injectReducer(store, { key: 'itemData', reducer: itemReducer })
      injectReducer(store, { key: 'todoList', reducer: todoListReducer })

      cb(null, Todo)
    }, 'todo')
  }
})

這裏將 store 和 reducer 綁定,將 store 注入到 /todo 的路由下的 props 中去

components/Todo.js

import React, { Component } from 'react'

export default class Todo extends Component {
  render () {
    return (
      <div>
        <input
          className='form-control'
          style={{ marginBottom: '10px' }}
          type='text' ref='input'
          value={this.props.itemData}
          onChange={(e) => this.handleChange(e)}
        />
        <button
          className='btn btn-primary btn-lg btn-block'
          style={{ marginBottom: '10px' }}
          onClick={(e) => this.handleSubmit(e)}
        >
          添加
        </button>
        <ul className='list-group'>
          {
            this.props.todoList.map((item, index) => {
              return <li
                className='list-group-item'
                key={index}
              >
                {item}
                <button
                  className='btn btn-default'
                  data-index={index}
                  onClick={(e) => this.handleDel(e)}
                >
                  ❌
                </button>
              </li>
            })
          }
        </ul>
      </div>
    )
  }

  handleChange (e) {
    const node = this.refs.input
    const text = node.value.trim()
    this.props.updateItem(text)
  }

  handleSubmit (e) {
    const node = this.refs.input
    const text = node.value.trim()
    this.props.addItem(text)
    this.props.updateItem('')
  }

  handleDel (e) {
    const index = e.target.getAttribute('data-index')
    this.props.delItem(Number(index))
  }
}

Todo.propTypes = {
  addItem: React.PropTypes.func.isRequired,
  itemData: React.PropTypes.string.isRequired,
  updateItem: React.PropTypes.func.isRequired,
  delItem: React.PropTypes.func.isRequired,
  todoList: React.PropTypes.array.isRequired
}

就是個簡單的 jsx,這裏後綴沒有寫成 .jsx 文件,由於 webpack 配置瞭解析規則,.js 文件也能夠正常解析,這裏的 onClick 事件寫成了 => 箭頭函數的形式,目的是爲了鎖定 this 的做用域,在 handle 函數內能夠拿到 this。

containers/TodoContainer.js

import { connect } from 'react-redux'
import { updateItem, addItem, delItem } from '../modules/todo'
import Todo from '../components/Todo'

const mapDispatchToProps = {
  updateItem,
  addItem,
  delItem
}

const mapStateToProps = (state) => ({
  itemData: state.itemData,
  todoList: state.todoList
})

export default connect(mapStateToProps, mapDispatchToProps)(Todo)

這裏經過一個 connect 方法,將組件的 props 進行綁定。

modules/todo.js

export const UPDATE_ITEM = 'UPDATE_ITEM'
export const ADD_ITEM = 'ADD_ITEM'
export const DELETE_ITEM = 'DELETE_ITEM'

export function updateItem (item) {
  return {
    type: UPDATE_ITEM,
    payload: item
  }
}

export function addItem (item) {
  return {
    type: ADD_ITEM,
    payload: item
  }
}

export function delItem (index) {
  return {
    type: DELETE_ITEM,
    payload: index
  }
}

export const actions = {
  updateItem,
  addItem,
  delItem
}

const ITEM_ACTION_HANDLERS = {
  [UPDATE_ITEM]: (state, action) => action.payload
}

const LIST_ACTION_HANDLERS = {
  [ADD_ITEM]: (state, action) => state.concat(action.payload),
  [DELETE_ITEM]: (state, action) => state.filter((item, index) => {
    return index !== action.payload
  })
}

export function itemReducer (state = '', action) {
  const handler = ITEM_ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}

export function todoListReducer (state = [], action) {
  const handler = LIST_ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}

這裏 reducer 運用兩個數組方法 concat 和 filter,分別作拼接和刪除操做,這裏意遵循 redux 的官方文檔在不修改原 state,而是返回新的 state。在底部將兩個 reducer 導出,綁定到 index.js 中。完成一個閉環,數據更新完成。

到這裏咱們的 todoList 完成了。

一邊看界面一邊看控制檯 redux store 的變化吧!代碼沒有晦澀難懂的點,一個 todoList 也足以說明列表操做的流程,這也是各大框架都以 todoList 爲 demo 的緣由。

總結

對於複雜的應用 redux 帶來的優點不贅述,本文的目的是講述如何快速開發 react + redux 應用,讓你們在配置環境上少踩坑,也經過一個簡單的例子,說明咱們應該怎麼更簡單的去使用 redux。動手寫起來吧,橙子的文章通常都看不出效果的,親手實現一遍會有意外收穫哦,上面代碼也是橙子本身踩過坑後寫出來的,各大神有什麼建議的地方儘管提出來!感謝閱讀。

文章出自 orange 的 我的博客 http://orangexc.xyz/
相關文章
相關標籤/搜索