深入淺出 Redux 的設計思想

IrvinQuisen 7年前發布 | 31K 次閱讀 Redux React

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

前言

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

前言(續)

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

1、你不需要使用Redux

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

2、你需要Redux

用戶的使用方式復雜
不同身份的用戶有不同的使用方式(比如普通用戶和管理員)
多個用戶之間可以協作
與服務器大量交互,或者使用了WebSocket
View要從多個來源獲取數據

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

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

一、Redux 的工作流程 (快遞思想)

看圖感覺有點亂,梳理一下就清楚了,首先對以上圖幾個名詞做個解釋,它們分別是干嘛用的,有什么功能

1、 Store(快遞公司)

Store 可以看成是一個容器,整個應用只能有一個 Store ,就好比整個應用只能指定京東快遞公司來運貨。

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

2、 State (快遞物件)

State :一狀態下的數據,一時間點下的數據。Store對象包含所有數據,想要得到某個時間點的數據,就要 對Store生成快照,得到一個數據集合,就叫做state。 store.getState() 可以得到state。 Redux規定一個State對應一個View,反之亦然。 就好比一個快遞物件只能給對應的主人,不能給其他人。

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

3、Action (快遞單)

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

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

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

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

store.dispatch(action);

5、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’的規則

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

當 State一旦發生變化,那么 store.subscribe() 就會監聽到自動執行。 好比收到了快遞(秒送,哈哈) 那收到快遞能干嘛呢,每錯,就是在這里重新渲染 View 更新View咯。

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

小結

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

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

如果使用第一節講述的 Redux 的那一套狀態管理方法來對 React進行數據管理,也是可以的,但就有些麻煩。 比如咱們要在最后自己定義更新View

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

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

1、React-Redux

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

2、connect()

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

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

因此,connect方法的API是這樣的:

import { connect } from 'react-redux'

const Comp = connect( mapStateToProps, mapDispatchToProps )(UI)</code></pre>

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。

3、 組件

上面多次提到 屬性映射到props組件上,子組件在通過props拿到數據或方法進行操作,那props對應的組件是誰呢, 也就是子組件的父組件是誰呢。 React-Redux 就提供了 組件 來充當所有UI組件的容器組件,所有UI組件都可以利用props屬性想 組件拿數據。而 組件的數據由 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組件就可以了

三、小例子:加減器

深入淺出 Redux 的設計思想

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

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;</code></pre>

    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')) );</code></pre>

你也可以 clone本項目運行調試

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

總結

以上是對 Redux 的學習和體會,很多資料來源于網上和大神的博客,如有疑問的話可以 issue, 覺得有幫助的話可以 star 一下本項目哦 

 

項目主頁:http://www.baiduhome.net/lib/view/home/1490081629027

 本文由用戶 IrvinQuisen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!