over 3 years ago

首先必需先安裝 NodeJS 和 npm
準備好底下 package.json 內容並執行 npm install 安裝套件

{
  "name": "myredux",
  "version": "0.0.1",
  "description": "a todo app that you'd never want to use",
  "main": "index.js",
  "scripts": {
    "test": " ",
    "start": "webpack-dev-server --devtool eval --progress --colors --hot"
  },
  "keywords": [
    "test"
  ],
  "author": "Jason Wang",
  "license": "ISC",
  "dependencies": {
    "immutable": "^3.7.5",
    "react": "^0.14.2",
    "react-dom": "^0.14.2",
    "react-redux": "^4.0.0",
    "redux": "^3.0.4",
    "webpack": "^1.12.6",
    "webpack-dev-server": "^1.12.1"
  },
  "devDependencies": {
    "babel-core": "^6.1.21",
    "babel-loader": "^6.2.0",
    "babel-preset-es2015": "^6.1.18",
    "babel-preset-react": "^6.1.18"
  }
}

建立 webpack.config.js

var path = require("path");
module.exports = {
  entry: './components/app.jsx',
  output: {
    path: path.resolve(__dirname, "build"),
    publicPath: "/",
    filename: "bundle.js"
  },
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        exclude: /(node_modules)/,
        loader: 'babel',
        query: {
          presets: ['react', 'es2015']
        }
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  }
};

建立 index.html

<!DOCTYPE html>
<html lang="zh-Hant-TW">
<head>
  <meta charset="UTF-8">
  <title>Todo</title>
</head>
<body>
  <div id="react"></div>
  <script src="bundle.js"></script>
</body>
</html>

建立 actions/index.js 編寫 action 設定

export function addTodo(todo) {
  return {
    type: 'addTodo',
    todo
  }
}

export function deleteTodo(index) {
  return {
    type: 'deleteTodo',
    index
  }
}

建立 reducers/todos.js 編寫 reducer 函數

import Immutable from 'immutable'

export default (state = Immutable.List(['Code More!']), action) => {
  switch (action.type) {
    case 'addTodo':
      return state.push(action.todo);
      case 'deleteTodo':
        return state.remove(action.index);
    default:
      return state;
  }
}

建立 store.js 執行 store createStore 將 actions 和 reducers 關聯在一起

import { createStore } from 'redux';
import todos from './reducers/todos'
export default createStore(todos)

建立 components/newTodo.jsx (dumb component) 執行新增Item

import React from 'react'

const newTodo = ({actions}) => (
  <div>
    <h3>New</h3>
    <input type="text" onKeyUp={
      e => {
        if(e.keyCode == 13){
          actions.addTodo(e.target.value);
          e.target.value = '';
        }
      }
    }/>
  </div>
)

export default newTodo

建立 components/todoItem.jsx (dumb component) 執行刪除Item

import React from 'react'

const todoItem = ({index, actions, value}) => (
   <p onClick={e=>{
     actions.deleteTodo(index);
     alert(index);
   }}  id={value}>{value}</p>
)

export default todoItem

建立 components/todos.jsx (smart component) 包含相關 dumb component
利用 connect 來監聽回傳一個新 store 的 state dispatch props 狀態
bindActionCreators 綁定 dispatch 到 action
直接供 dumb component 執行

import React , { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'

import NewTodo from './newTodo'
import TodoItem from './todoItem'
import * as TodoActions from '../actions'

class Todos extends Component {
  render() {
    const {todos, actions} = this.props;
    return (
      <div>
        <h1>Todos</h1>
        <NewTodo actions={actions}/>
        {todos.map((todo, index) => <TodoItem key={todo} index={index} actions={actions} value={todo} />)}
      </div>
    )
  }
}
function mapStateToProps(todos) {
  return {
    todos
  }
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(TodoActions, dispatch)
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Todos)

建立 components/app.jsx 包含 Provider component 及 root-component

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import store from '../store'
import Todos from './todos'

let reactElement = document.getElementById('react')
render(
  <Provider store={store}>
    <Todos />
  </Provider>,
  reactElement
)

整個應用的 state 被存在在一棵 object tree 並且這個 object tree 只存在唯一一個 store 中
唯一改變 state 的方法就是 action
通常會在 action 呼叫 api

為了描述 action 如何改變 state tree 必須編寫 reducers
Reducer 只是一些纯函数,它接收先前的 state 和 action,並返回新的 state
其實很簡單,函數結構就是(previousState, action) => newState

Provider component
使用 Provider 包覆的 root component 才能使用 connect() 來獲得 stroe 的狀態

root component
將接收的 store 給底下 smart component 執行 connect() 來監聽回傳一個新 stroe 的 state dispatch props

smart component
1.透過 props 配置 action 給底下 dumb component 的 props
2.不會有自己的 CSS
3.不會產生任何的 Dom 只交由 dumb component 去產生
4.使用 classes 方式建構 component

dumb component
1.不會直接引用 actions
2.藉由 smart component 給予的 props action 做呼叫, 幫助執行各種 action 邏輯變化
3.擁有自身的 CSS
4.盡量使用 Stateless functional component 只處理 props, 除非你要使用 state 或其他 lifecycle hooks才改用 classes 方式建構 component

<Provider-component>
   <root-component>
     <smart-component>
        <dumb-component />
     </smart-component>  
   </root-component>    
</Provider-component>

Provider-component 跟 root-component 會放主要js 內
而 smart-component 跟 dumb-component 會被配置在 root-component js 內

大致流程
component ---> action ---> reducer ---> store ---> component

參考
http://rhadow.github.io/2015/07/30/beginner-redux/
https://reactjsnews.com/your-first-redux-app
http://redux.js.org/index.html
https://github.com/reactjs/react-redux/blob/master/docs/api.md

← 設定 React ES6 環境 採用 Webpack 和 Babel React 簡易 unit-test 使用 Chai 與 mocha →
 
comments powered by Disqus