react、react-router、redux 也許是最佳小實踐2

上一篇:react、react-router、redux 也許是最佳小實踐1css

加入 redux

React 在組件之間流通數據.更確切的說,這被叫作「單向數據流」——數據沿着一個方向從父組件流到子組件。因爲這個特性,對於沒有父子關係的兩個組件之間的數據交流就變得不是那麼顯而易見。這裏 Redux 就排上用場了。Redux提供了一個解決方案,經過將應用程序全部的狀態都存儲在一個地方,叫作「store」。而後組件就能夠「dispatch」狀態的改變給這個store,而不是直接跟另外的組件交流。全部的組件都應該意識到狀態的改變能夠「subscribe」給store。以下圖:
Alt textreact

原理講完,下面開始加入代碼。
先看看一個小例子。git

開始以前,須要先用 Redux.createStore() 建立一個store,而後將全部的reducer做爲參數傳遞進去,讓咱們看一下這個只傳遞了一個reducer的小例子:github

var userReducer = function(state, action) {
  if (state === undefined) {
    state = [];
  }
  if (action.type === 'ADD_USER') {
    state.push(action.user);
  }
  return state;
}

var store = Redux.createStore(userReducer);

store.dispatch({
  type: 'ADD_USER',
  user: {name: 'xiaoming'}
});

上面的程序幹了些什麼呢:數據庫

  1. 這個store只由一個reducer建立。redux

  2. 這個reducer 初始化狀態的時候使用了一個空數組 。*segmentfault

  3. 在被分派的這個action裏面使用了新的user對象。數組

  4. 這個reducer將這個新的user對象附加到state上,並將它返回,用來更新store。服務器

*在這個例子裏reducer實際上被調用了兩次 —— 一次是在建立store的時候,一次是在分派action以後。 react-router

當store被建立以後,Redux當即調用了全部的reducer,而且將它們的返回值做爲初始狀態。第一次調用reducer傳遞了一個 undefined 給state。通過reducer內部的代碼處理以後返回了一個空數組給這個store的state做爲開始。

全部的reducer在每次action被分派以後都會被調用。由於reducer返回的狀態將會成爲新的狀態存儲在store中,因此 Redux老是但願全部的reducer都要返回一個狀態。

在這個例子中,reducer第二次的調用發生在分派以後。記住,一個被分派的action描述了一個改變狀態的意圖,並且一般攜帶有數據用來更新狀態。這一次,Redux將當前的狀態(仍舊是空數組)和action對象一塊兒傳遞給了reducer。這個action對象,如今有了一個值爲1ADD_USERtype屬性, 讓reducer知道怎樣改變狀態。

正式redux登場

src 下面建立一個 reduxactionsdata(存放一些初始數據)文件夾,而後在 data文件夾下面建立一個db.js,這個文件寫上一些初始的數據:

src/data/db.js

const data = [
    {
        id: 1,
        title: '明天要去打醬油',
        content: '系呀系呀咱們一塊兒打醬油'
    },
    {
        id: 2,
        title: '週末去書吧讀書',
        content: '書籍是人類進步的階梯'
    },
    {
        id: 3,
        title: '備份一下數據庫',
        content: '備份服務器的數據庫,通常都是分開的,分佈式數據庫'
    },
    {
        id: 4,
        title: '週五記得把被子洗了',
        content: '洗杯子被子被子被子'
    },
    {
        id: 5,
        title: '計劃五',
        content: '計劃五內容'
    }
]

export default data

好了,初始的數據咱們有了,下面就是建立 store 了,在redux文件夾下面,建立一個planlist.js文件,這個文件就是操做 store 的動做 action集合處理的數據,這時候咱們會去action文件夾下面新建,action-type.jsplan.js,代碼以下:

src/action/action-type.js

export const ADD = 'ADD';
export const DELECT = 'DELECT';
export const SHOW = 'SHOW';

src/action/plan.js

import * as types from './action-type.js';
// 添加計劃
export function addPlan(item) {
  return {
    type: types.ADD,
    item
  };
}
// 刪除計劃
export function deletePlan(id) {
  return {
    type: types.DELECT,
    id
  };
}
// 顯示隱藏彈層
export function show(show) {
  return {
    type: types.SHOW,
    show
  };
}

action 咱們都定義好了如今咱們就能夠改變 store了。寫好咱們的 reducer

src/redux/planlist.js

import * as types from '../actions/action-type.js';
import data from '../data/db.js'
const initialState = {
  show: false, // 是否顯示彈出
  planlist: data // 初始的計劃表
};

const planReducer = function(state = initialState, action) {
    let list = state.planlist;
  switch(action.type) {
    // 添加計劃
    case types.ADD:
        list.push(action.item);
      return Object.assign({}, state, { planlist: list });
    // 刪除計劃
    case types.DELECT:
      let newstate = list.filter((item) => item.id != action.id);
      return Object.assign({}, state, { planlist: newstate });;
     // 顯示、隱藏彈出層
     case types.SHOW:
         return Object.assign({}, state, { show: action.show });
  }
  return state;

}

export default planReducer;

在redux 下面再建立reducers.jsstore.js

src/redux/reducers.js

import { combineReducers } from 'redux';

// Reducers
import planlist from './planlist';

// Combine Reducers
var reducers = combineReducers({
    planlist: planlist
});

export default reducers;

src/redux/store.js

import { createStore } from 'redux';
import reducers from './reducers.js';

const store = createStore(reducers);
export default store;

這會咱們的 store 就徹底的建立好了,下面就是把 store 跟咱們的組件,徹底的結合起來。這就用到 react-redux 的 connect 模塊。
這個東西 就是把組件跟 store 鏈接起來的模塊。

而後在,App.js加入咱們的。store

src/App.js

import React, { Component } from 'react'
import {
  BrowserRouter as Router,
  Route,
  Link
} from 'react-router-dom'
// 引入 store
import { Provider, connect } from 'react-redux';
import store from './redux/store.js'
import logo from './logo.svg'
import Plan from './components/plan.js'
import Home from './components/home.js'
import Popup from './components/pupop.js'
import TestRouter from './components/testrouter.js'
import Detail from './components/detail.js'
import './App.css'
import './components/comment.css'
import createHistory from 'history/createBrowserHistory'
const history = createHistory()
class App extends Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
        // store的掛載
       <Provider store={store}>
        <div className="App">
            <div className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <h2 className='App-title'>Welcome to React Plan</h2>
            </div>
            <div>
              <Router history = {history}>
                 <div className="contentBox">
                    <ul className="nav">
                      <li><Link to="/">首頁</Link></li>
                      <li><Link to="/plan">計劃表</Link></li>
                      <li><Link to="/test">二級路由</Link></li>
                    </ul>
                    <div className="content"> 
                      <Route exact path="/" component={Home}/>
                      <Route path="/plan" component={Plan}/>
                      <Route path="/test" component={TestRouter}/>
                      <Route path="/detail/:id" component={Detail}/>
                    </div>
                </div>
              </Router>
            </div>
            <Popup/>
        </div>
      </Provider>
    );
  }
}

export default App

而後在 plan.js鏈接 store

src/component/plant.js

import React, { Component } from 'react'
import { connect } from 'react-redux';
import store from '../redux/store.js';
// 引入 定義的 action
import {show, deletePlan} from '../actions/plan.js';

class Plan extends Component {
  constructor(props) {
      super(props);
  }
  // 顯示彈出
  show () {
    let b = this.props.planlist.show;
    store.dispatch(show(!b));
  }
  // 刪除計劃
  delete (id) {
      store.dispatch(deletePlan(id));
  }
  // js 跳轉路由
  detail (id) {
      this.props.history.push(`/detail/${id}`)
  }
    render () {
        return (
            <div>
                <div className="plant">
                    <h3>計劃表</h3>
                    <p onClick={this.show.bind(this)}>添加計劃</p>
                </div>
                <table className="planlist">
                    <thead>
                        <tr>
                            <th>標題</th>
                            <th>操做</th>
                        </tr>
                    </thead>
                    <tbody>
                        {
                            this.props.planlist.planlist.map((item, index) => {
                                return (
                                    <tr key={index}>
                                        <td className="plan-title" onClick={this.detail.bind(this, item.id)}>{item.title}</td>
                                        <td className="plan-delect" onClick={this.delete.bind(this, item.id)}>刪除</td>
                                    </tr>
                                )
                            })
                        }
                    </tbody>
                </table>
            </div>
        )
    }
}

const mapStateToProps = function(store) {
  return {
    planlist: store.planlist
  };
};
// 鏈接 store,做爲 props
export default connect(mapStateToProps)(Plan);

同理下面的 js,都是用這個模塊鏈接

src/component/detail.js

import React, { Component } from 'react'
import { connect } from 'react-redux';
import store from '../redux/store.js';


class Detail extends Component {
    constructor(props) {
        super(props);
        // 根據路由 id 跟 store 作過濾
        let item = props.planlist.planlist.filter((data) => data.id == props.match.params.id)
        console.log(item)
        this.state = {
            plan: item[0]
        }
    }
    render() {
        return (
            <div style={{padding: '20px'}}>
                <h3>計劃詳情</h3>
                <p>id: {this.state.plan.id}</p>
                <p>標題: {this.state.plan.title}</p>
                <p>內容: {this.state.plan.content}</p>
            </div>

        )
    }
}


const mapStateToProps = function(store) {
  return {
    planlist: store.planlist
  };
};
// 鏈接 tore 和組件
export default connect(mapStateToProps)(Detail);

src/component/popup.js

import React, { Component } from 'react'
import { connect } from 'react-redux';
import store from '../redux/store.js';
import {show, addPlan} from '../actions/plan.js';

class Pupop extends Component{
  constructor (props) {
    super(props)
    this.state = {
      id: '',
      title: '1',
      content: '1'
    }
  }
  // 取消按鈕操做
  close () {
    let b = this.props.planlist.show;
    this.setState({
      id: '',
      title: '',
      content: ''
    })
    store.dispatch(show(!b));
  }
  // 輸入框事件
  handleChage (str, e) {
    this.setState({
      id: Math.ceil(Math.random()*10000),
      [str]: e.target.value
    })
  }
  // 確認操做
  conform () {
    store.dispatch(addPlan(this.state));
    this.setState({
      id: '',
      title: '',
      content: ''
    })
    this.close();
  }

  render() {
    let self = this;
    return (
      <section className="popup" style={this.props.planlist.show ? {} : {display: 'none'}}>
        <div className="pbox">
          <span className="close" onClick={this.close.bind(this)}>X</span>
          <div>
            <h4>計劃標題</h4>
            <input onChange={this.handleChage.bind(this, 'title')} value={this.state.title} placeholder="請輸入計劃標題"/>
          </div>
          <div>
            <h4>計劃內容</h4>
            <textarea onChange={this.handleChage.bind(this, 'content')} value={this.state.content} placeholder="請輸入計劃內容" rows="3"></textarea>
          </div>
          <div className="pBtn">
            <span onClick = {this.close.bind(this)}>取消</span>
            <span onClick = {this.conform.bind(this)}>確認</span>
          </div>
        </div>
      </section>
    )
  }
}

const mapStateToProps = function(store) {
  return {
    planlist: store.planlist
  };
};
// 鏈接 store和組件
export default connect(mapStateToProps)(Pupop);

完工。github地址

相關文章
相關標籤/搜索