深刻淺出 Redux 的設計思想

lesson-2 主要內容: Redux 的設計思想

項目地址 :https://github.com/ZengTianSh...css

前言

Redux是什麼呢?一個狀態管理工具。那是幹嗎用的呢?都知道,React能夠進行單頁應用的開發,能夠對頁面
中各個模塊進行分割造成組件,而組件之間就避免不了事件的傳遞或數據的交互,那Redux就是用來對這些組件
的狀態進行管理的。就比如買家和賣家,快遞是交給第三方(Redux)去完成。react

前言(續)

也行你會說,React不是有本身的一套數據傳遞和事件管理機制麼,何須要引入第三方來插一腳,那麼麻煩呢。
這裏有一些場景來講明引用 React 是否必要:git

一、你不須要使用Redux

用戶的使用方式很是簡單
用戶之間沒有協做
不須要與服務器大量交互,也沒有使用 WebSocket
視圖層(View)只從單一來源獲取數據

二、你須要Redux

用戶的使用方式複雜
不一樣身份的用戶有不一樣的使用方式(好比普通用戶和管理員)
多個用戶之間能夠協做
與服務器大量交互,或者使用了WebSocket
View要從多個來源獲取數據

固然啦,本篇章講述的例子是不須要用到 Redux 這一套狀態管理工具的,但爲了講解就須要簡單的例子來講明github

用一個簡單的例子來深刻淺出的理解 Redux的設計思想:
一個簡單的加減器,點擊加號加一,點擊減號減一npm

圖片描述

1、Redux 的工做流程 (快遞思想)

圖片描述

看圖感受有點亂,梳理一下就清楚了,首先對以上圖幾個名詞作個解釋,它們分別是幹嗎用的,有什麼功能redux

一、 Store(快遞公司)

Store 能夠當作是一個容器,整個應用只能有一個 Store ,就比如整個應用只能指定京東快遞公司來運貨。服務器

import {createStore, combineReducers, applyMiddleware} from 'redux';
const store = createStore(reducer);

二、 State (快遞物件)

State :一狀態下的數據,一時間點下的數據。Store對象包含全部數據,想要獲得某個時間點的數據,就要
對Store生成快照,獲得一個數據集合,就叫作state。 store.getState()能夠獲得state。
Redux規定一個State對應一個View,反之亦然。 就比如一個快遞物件只能給對應的主人,不能給其餘人。app

import {createStore, combineReducers, applyMiddleware} from 'redux';
const store = createStore(reducer);
store.getState()

三、Action (快遞單)

買家要買東西怎麼辦,固然要先下單啦。用戶只能操做 View(好比對view進行點擊),用戶是接觸不到State的,
那State的變化對應View的變化,這就須要View經過一個Action對象來通知State的變化。就比如經過一個
快遞下單(發送一個Action),纔有接下去的物流等一系列操做不是嗎。
Action是一個自定義對象,其中type屬性是必現的less

cost action = {
  type:'btnClick',
  msg:'信息字符串,不是必現'
}

四、store.dispatch() (給快遞公司貨單)

store.dispatch() 是view發出Action的惟一方法,store.dispatch()接受一個Action對象,將他發出去。
如今還沒發貨,只是把訂單信息給京東快遞而已,京東是自營企業,有本身的物流倉庫和物流中心,搜到訂單信息
再根據訂單來發貨。dom

store.dispatch(action);

五、Reducer (包裝貨物)

Store 收到一個Action後,必須給出一個新的State,這樣view纔會發生變化,而新的State的計算過程就是
Reducer來完成的。
就想收到一個訂單(Action)後,須要根據訂單來選取貨物,進行包裝。
Reducer是一個自定義函數,它接受Action和當前的State做爲參賽,返回一個新的State

const reducer = (state=defaultState,action) =>{
  switch (action.type) {
  case 'btnClick':
    return state + action.msg +'更新';
  case '其餘type':
    return state + ‘其餘action.msg’;
  /*
    可添加更多的 case type 來匹配不一樣的Action
  */
  default:
    return state;
  }  
}

接下來 咱們把這套處理訂單的規則(Reducer)給快遞公司(Store),之後有訂單隻須要根據這套規則來發貨就好了

import { createStore } from 'redux';
// store.dispatch 方法會觸發 Reducer 的自動執行
const store = createStore(reducer);

也許你會疑問那須要發送不一樣的 Action怎麼辦 ,沒錯就是增長訂單規則就能夠了,往 Reducer 裏面增長 case ’type’的規則

六、store.subscribe() (接受快遞)

當 State一旦發生變化,那麼 store.subscribe() 就會監聽到自動執行。 比如收到了快遞(秒送,哈哈)
那收到快遞能幹嗎呢,每錯,就是在這裏從新渲染 View 更新View咯。

let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
  render(){
      更新view !!!
  }
);
// 也能夠取消訂閱(監聽)
unsubscribe();

小結

圖片描述

下面講解 Redux 在 React下的應用 -- React-Redux

2、Redux 在 React下的應用 (React-Redux)

若是使用第一節講述的 Redux 的那一套狀態管理方法來對 React進行數據管理,也是能夠的,但就有些麻煩。
好比我們要在最後本身定義更新View

store.subscribe(() =>
  console.log(store.getState())
  render(){  // React 的 render()方法
      更新view !!!
  }
);

爲了使用方便,Redux的做者封裝了一個React專用的庫 React-Redux

一、React-Redux

React-Redux 將組件分爲兩大類,UI組件和容器組件。UI組件只負責UI的呈現,而容器組件用來管理數據和事件。
那怎麼把交互和UI聯繫起來呢,那就使用 React-Redux 提供的 connect 方法。

二、connect()

React-Redux 提供的 connect 方法。就是將 UI組件個容器組件聯繫起來,那規則是什麼呢?
須要有:

輸入邏輯:外部數據(state對象)轉爲 UI組件的參數
輸出邏輯:用戶發出的動車 (Action對象)從UI組件傳遞出去

所以,connect方法的API是這樣的:

import { connect } from 'react-redux'

const Comp = connect(
  mapStateToProps,
  mapDispatchToProps
)(UI)

connect方法接受兩個參數:mapStateToProps 和 mapDispatchToProps ,前者負責輸入邏輯,將state映射到
UI組件的參數 props ,後者負責輸出邏輯,將用戶對UI的操做映射成Action。

(1)、mapStateToProps()

mapStateToProps是一個函數,創建一個 state對象到UI組件的props對象的映射關係!是映射到UI組件的props
對象上(關鍵點,多提醒一下,下面會作解釋)。

const mapStateToProps = (state) => {
    return {
        count: state.count
    }
};

mapStateToProps 接受參數state,state的字段(state.count)賦值給 count 屬性,而count屬性是UI組件的
同名參數。
mapStateToProps會定義 Store,每當 state更新就會自動執行這個方法,那自動執行這個方法怎麼就會更新UI呢,
UI更新一個來自自身的 this.state 變化,還有一個來自 this.props變化都會觸發React組件從新render(),
而 就上面例子 mapStateToProps 接受 state 的變化從而返回一個 賦值 count屬性,而這個屬性是對應UI組件的
props對象的映射,props變化天然會帶動UI組件的更新。

(2)、mapDispatchToProps()

mapDispatchToProps 是 connect的第二個參數,也是一個函數(還能夠是一個對象)。mapDispatchToProps做爲函數,
應該返回一個對象,該對象的每一個鍵值對都是一個映射,定義了 UI 組件的參數怎樣發出 Action。又是一個映射到UI組件的
props參數上!!!

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        increase: (...args) => dispatch(actions.increase(...args)),
        decrease: (...args) => dispatch(actions.decrease(...args))
    }
};

上面 mapDispatchToProps函數接受兩個參數,返回一個對象,注意返回對象的屬性是對應UI組件的props參數的。
也就是UI組件怎麼派發一個Action呢,那就是 UI組件調用 props.increase 就會執行 dispatch(Action)
操做,從而派發一個Action。

三、<Provider> 組件

上面屢次提到 屬性映射到props組件上,子組件在經過props拿到數據或方法進行操做,那props對應的組件是誰呢,
也就是子組件的父組件是誰呢。
React-Redux 就提供了<Provider> 組件 來充當全部UI組件的容器組件,全部UI組件均可以利用props屬性想
<Provider> 組件拿數據。而<Provider> 組件的數據由 store提供。

render(
    <Provider store={store}>
        <Index></Index>
    </Provider>,
    document.body.appendChild(document.createElement('div'))
);

小結

由下圖能夠看出 React-Redux 的大體工做原理,能夠看出UI組件確實只負責UI部分,只經過props參數拿數據和對外
派發數據,而沒有多作業務上的邏輯。而業務邏輯和數據呈現交給了容器組件,UI組件和容器組件是經過 connect()
方法連接的,內部是經過 mapStateToProps 和mapDispatchToProps進行數據傳遞的。整個應用的數據都會通過
Store這個中央處理器的處理。因此不一樣UI組件之間的數據交互能夠把數據都丟給Store這個中央處理器處理,Store
再把處理好的數據回傳給各個UI組件就能夠了

圖片描述

### 3、小例子:加減器

圖片描述

用一段代碼來鞏固前面所學習的知識。實踐強化嘛!哈哈

Main.jsx:

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

/**
* 定義一個 Main組件
*/
class Main extends Component{
    constructor(){
        super();
        this.pClick =() =>{
            console.log('sssssssss');
        };
        this.state = {
            num:0,
            age:666
        }
    }
    render(){
        // 拿到 this.props 參數
        const {count, increase, decrease} = this.props;
        return(
            <div>
                <p className="lesson-2">React lesson-2</p>
                <p>
                    ---------------------------------
                </p>
                <div className="count">
                    <div>計數:{this.props.count}次</div>
                    <span className="btn" onClick={increase}>+</span>
                    <span className="btn" onClick={decrease}>-</span>
                </div>
            </div>
        )
    }
}
/**
* 用來給 組件傳遞數據
* @param state
*/
const mapStateToProps = (state) => {
    return {
        count: state.count
    }
};
/**
*用來組件給 容器組件派發數據
* @param dispatch 派發 Action
* @param ownProps 組件自身的 props參數
*/
const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        increase: (...args) => dispatch(actions.increase(...args)),
        decrease: (...args) => dispatch(actions.decrease(...args))
    }
};
/**
* actions
*/
const actions ={
    increase:() => {
      return {type: 'INCREASE'}
    },
    decrease: () => {
      return {type: 'DECREASE'}
    }
};
/**
* 鏈接 UI組件 和 容器組件
* @param mapStateToProps     輸入邏輯
* @param mapDispatchToProps  輸出邏輯
*/
const Comp = connect(mapStateToProps, mapDispatchToProps)(Main);
/**
*  輸出一個容器組件
*/
export default Comp;

App.js

import React,{Component,PropTypes} from 'react';
import ReactDOM, {render} from 'react-dom';
import {Provider,connect} from 'react-redux';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import Index from './Component/Main.jsx';
import './Style/comm.scss'

const store = createStore(reducer);
//監聽state變化
store.subscribe(() => {
    //console.log(store.getState())
});
const reducer = (state = {count: 0}, action) => {
    switch (action.type){
        case 'INCREASE': return {count: state.count + 1};
        case 'DECREASE': return {count: state.count - 1};
        default: return state;
    }
}
render(
    <Provider store={store}>
        <Index></Index>
    </Provider>,
    document.body.appendChild(document.createElement('div'))
);

你也能夠 clone本項目運行調試

clone git@github.com:ZengTianShengZ/react-lesson.git
cd lesson-2
npm install
npm run hot

總結

以上是對 Redux 的學習和體會,不少資料來源於網上和大神的博客,若有疑問的話能夠 issue,
以爲有幫助的話能夠 star 一下本項目哦 ^-^

項目地址: https://github.com/ZengTianSh...

相關文章
相關標籤/搜索