理解Redux數據流

對於剛剛接觸redux的前端開發者來講,要想一會兒直接理解redux內部的數據流向可能有點不太現實。但通過一段時間的總結和上手一個簡單的redux項目,相信咱們對redux內部的數據流向及內部原理就可以掌握得比較清楚了。

Redux簡介

如今咱們就從Redux是什麼,Redux的三大原則和Redux的核心API開始介紹Redux,並說明Redux如何與React結合使用,以及它在Flux基礎上的改變。前端

Redux是什麼

咱們都知道Flux它自己既不是庫,也不是框架,而是一種應用的架構思想。而Redux呢,它的核心代碼能夠理解成一個庫,但同時也強調和Flux相似的架構思想。
從設計上看,Redux參考了Flux的設計,可是對Flux許多冗餘的部分作了簡化,同時將函數式編程的思想融合其中。
很是有意思的是,Redux是從一個實驗開始的,做者並無想到Redux會變得如此重要又被普遍使用,他只是爲了經過Flux思想解決他的熱重載及時間旅行的問題而已。
Redux自己很是簡單,它的設計思想與React有殊途同歸之妙,均是但願用最少的API實現最核心的功能。Redux自己只把本身定位成一個可預測的狀態容器
「Redux」自己指redux這個npm包,它提供若干API讓咱們使用reducer建立store,並可以更新store中的數據或獲取store中最新的狀態。而「Redux應用」則是指使用了redux這個npm包並結合了視圖層實現(如React)及其餘前端應用必備組件(路由庫,請求庫等)組成的完整的類Flux思想的前端應用。react

Redux三大原則

想要理解Redux,必需要知道Redux設計和使用的三大原則。npm

  • 單一數據源
    在傳統的MVC框架中,咱們能夠根據須要建立無數個Model,而Model之間能夠互相監聽、觸發事件甚至循環或嵌套觸發事件,這些在Redux中都是不容許的。
    由於在Redux的思想裏,一個應用永遠只有惟一的數據源。咱們的第一反應多是:若是一個複雜應用,強制要求惟一的數據源豈不是會產生一個特別龐大的JavaScript對象。
    實際上,使用單一的數據源的好處在於整個應用狀態保存在一個對象中,這樣咱們隨時能夠提取出整個應用的狀態進行持久化。此外,這樣的設計也爲服務端渲染提供了可能。
    至於咱們擔憂的數據源對象過於龐大的問題,咱們能夠經過combineReducers化解。編程

  • 狀態是隻讀的
    在Redux中,咱們並不會本身用代碼來定義一個store。取而代之的是,咱們定義一個reducer,它的功能是根據當前觸發的action對當前應用的狀態進行迭代,這裏咱們並無直接修改應用的狀態,而是返回了一份全新的狀態。
    Redux提供的createStore方法會根據reducer生成store。最後,咱們能夠利用store.dispatch方法來達到修改狀態的目的。redux

  • 狀態修改均由純函數完成
    在Redux裏,咱們經過定義reducer來確認狀態的修改,而每個reducer都是純函數,這意味着它沒有反作用,即接受必定的輸入,一定會獲得必定的輸出
    這樣設計的好處不只在於reducer裏對狀態的修改變得簡單、純粹、可測試,更有意思的是,Redux利用每次新返回的狀態生成酷炫的時間旅行調試方式,讓跟蹤每一次由於觸發action而改變狀態的結果成爲了可能後端

Redux核心API

Redux的核心是一個store,這個store由Redux提供的createStore方法生成。要想生成store,必須傳入reducers。
在Redux裏,負責響應action並修改數據的角色是reducer。reducer本質上是一個純函數,它有兩個參數state和action。reducer的職責就是根據state和action計算出新的state。
在實際應用中,reducer在處理state時,還須要有一個特殊的非空判斷。很顯然,reducer第一次執行的時候,並無任何的state,而reducer的最終職責是返回新的state,所以須要在這種特殊狀況下返回一個定義好的state。這也就是咱們會定義一個defaultState的緣由。
下面來介紹一下Redux中最核心的API--createStore:bash

import { createStore } from 'redux';
const store = createStore(reducers);
複製代碼

經過createStore方法建立的store是一個對象,它自己包含4個方法。架構

  • getState():獲取store中當前的狀態。
  • dispatch(action):分發一個action,並返回這個action,這是惟一能改變store中數據的方式。
  • subscribe(listener):註冊一個監聽者,它在store發生變化時被調用。
  • replaceReducer(nextReducer):更新當前store裏的reducer,通常只會在開發模式中調用該方法。
    在實際開發中,咱們最經常使用的是getState()和dispatch()這兩個方法。至於subscribe()和replaceReducer()方法,通常會在Redux與某個系統作橋接的時候使用。

簡單的項目實踐

在當前的項目中,模塊化開發已經成爲主流。我的認爲在模塊化開發中,最重要的即是目錄結構的設計。框架

在這裏咱們對目錄進行了比較細緻的劃分:ide

  • components:該目錄下存放的是組件,好比開發網站類的項目實常常用到的Header公共組件。
  • pages: 該目錄下存放的是網頁,系統中各個網頁放在該目錄下。好比首頁和詳情頁等。
  • store:該目錄存放actions、reducers、constants(常量文件:存放dispatch要用到的常量)和index(建立store)。

我的認爲在Redux項目中這樣的目錄設計是比較符合模塊化設計思想的目錄。(仁者見仁智者見智)
接下來我就介紹一下Redux具體的步驟和流程。(已請求簡單列表數據爲例)

第1步:reducer

store中的數據是由reducer決定的,因此第一步先完成reducer。

const defaultState = {
  homeList: []
}
export default function(state = defaultState, action) {
  switch(action.type) {
    case GET_HOME_LIST:
      return {
        homeList: action.homeList
      };
    default:
      return defaultState
  }
}
複製代碼

第2步: action

export const getHomeList = {
  type: GET_HOME_LIST,
  // 模擬從後端接收到的假數據
  homeList: [0,1,2,3]
}
複製代碼

第3步: constant

一個應用的常量應該是惟一的,把常量放在一個文件夾裏同時按照模塊劃分,有利於防止變量名的衝突。

// home
export const GET_HOME_LIST = 'GET_HOME_LIST';
複製代碼

第4步: index

該文件是用來建立store的。

import { createStore, 
  combineReducers} from 'redux'
import HomeReducer from './reducers/home/index';
// 多個 reducer 合併成一個
const rootReducer = combineReducers({
  home: HomeReducer
})
// 建立 store 只能接收到 一個 reducer 
// 因此 建立以前 合併一下
export default createStore(rootReducer)
複製代碼

第5步: 使用store

class App extends ImmutableComponent {
  render() {
    return (
      // store成爲全局變量,任何組件均可以訪問了
      <Provider store={store}>
        <BrowserRouter>
            <Header />
            <Route path="/" component={Home} exact/>
            <Route path="/detail" component={Detail} />
        </BrowserRouter>
      </Provider>
    );
  }
}
複製代碼
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getHomeList } from '../../store/actions/home';
class Home extends Component {
  componentDidMount() {
    this.props.fetchHomeList();
  }
  state = {}
  render() {
    return ( <div>
      home
      length: { this.props.homeList.length }
    </div> );
  }
}
// 獲取數據
// state:整個store,home頁面,只要home模塊,過濾一下
// 過濾完結果(return)都會由 connect 傳給你組件的 props
const mapStateToProps = (state) => {
  return {
    homeList: state.home.homeList
  }
}
// 用戶操做UI引發頁面變化
// 發起一個action
// dispacth行爲connect傳給組件
const mapDispatchToProps = (dispatch) => {
  return {
    fetchHomeList() {
      dispatch(getHomeList)
    }
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
複製代碼

但願這個流程能對咱們學習Redux有必定的幫助。 [注] 以上代碼只是一個Redux的流程,並非完整的代碼。
相關文章
相關標籤/搜索