react之redux狀態管理


1、傳統MVC框架的缺陷

模型(model)-視圖(view)-控制器(controller)的縮寫
V即View視圖:用戶看到並與之交互的界面。

M即Model模型是管理數據:很多業務邏輯都在模型中完成。在MVC的三個部件中,模型擁有最多的處理任務。

C即Controller控制器:接受用戶的輸入並調用模型和視圖去完成用戶的需求,控制器本身不輸出任何東西和做任何處理。它只是接收請求並決定調用哪個模型構件去處理請求,然后再確定用哪個視圖來顯示返回的數據。
缺點
MVC框架的數據流很理想,請求先到Controller, 由Controller調用Model中的數據交給View進行渲染.
但是在實際的項目中,又是允許Model和View直接通信的。

2、Flux

在2013年,Facebook讓React亮相的同時推出了Flux框架,React的初衷實際上是用來替代jQuery的,Flux實際上就可以用來替代Backbone.jsEmber.js等一系列MVC架構的前端JS框架。

其實FluxReact里的應用就類似於Vue中的Vuex的作用,但是在Vue中,Vue是完整的mvvm框架,而Vuex只是一個全局的插件。

React只是一個MVC中的V(視圖層),只管頁面中的渲染,一旦有數據管理的時候,React本身的能力就不足以支撐復雜組件結構的項目,在傳統的MVC中,就需要用到Model和Controller。Facebook對於當時世面上的MVC框架並不滿意,於是就有了Flux, 但Flux並不是一個MVC框架,他是一種新的思想

  • View: 視圖層
  • ActionCreator(動作創造者):視圖層發出的消息(比如mouseClick)
  • Dispatcher(派發器):用來接收Actions、執行回調函數
  • Store(數據層):用來存放應用的狀態,一旦發生變動,就提醒Views要更新頁面

Flux的流程:

  1. 組件獲取到store中保存的數據掛載在自己的狀態上
  2. 用戶產生了操作,調用actions的方法
  3. actions接收到了用戶的操作,進行一系列的邏輯代碼、異步操作
  4. 然后actions會創建出對應的action,action帶有標識性的屬性
  5. actions調用dispatcher的dispatch方法將action傳遞給dispatcher
  6. dispatcher接收到action並根據標識信息判斷之后,調用store的更改數據的方法
  7. store的方法被調用后,更改狀態,並觸發自己的某一個事件
  8. store更改狀態后事件被觸發,該事件的處理程序會通知view去獲取最新的數據

3、Redux

注意:flux、redux都不是必須和react搭配使用的,因為flux和redux是完整的架構,在學習react的時候,只是將react的組件作為redux中的視圖層去使用了。

React 只是 DOM 的一個抽象層,並不是 Web 應用的完整解決方案。
有兩個方面,它沒涉及:

  • 代碼結構
  • 組件之間的通信

2013年 Facebook 提出了 Flux 架構的思想,引發了很多的實現。2015年,Redux 出現,將 Flux 與函數式編程結合一起,很短時間內就成為了最熱門的前端架構。

使用場景設計思路

不需要redux情況

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

需要redux情況

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

從組件層面考慮,什么樣子的需要Redux:

  • 某個組件的狀態,需要共享
  • 某個狀態需要在任何地方都可以拿到
  • 一個組件需要改變全局狀態
  • 一個組件需要改變另一個組件的狀態

Redux的設計思想:

  1. Web 應用是一個狀態機,視圖與狀態是一一對應的。
  2. 所有的狀態,保存在一個對象里面(唯一數據源)。

Redux的使用的三大原則:

  1. store:單一數據源
    整個應用的 state 被儲存在一棵對象結構中,並且這個對象結構只存在於唯一一個 store 中
    不能直接去修改此數據源中的數據(數據深拷貝)
  2. State:是只讀的
    state redux中的state
    唯一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通對象。
    action只能是一個對象
  3. reducer:使用純函數(reducer)來執行修改
    為了描述 action 如何改變 state tree ,你需要編寫 reducer,reducer只是一些純函數,它接收先前的 state 和 action,並返回新的 state

Redux框架的使用


Reducer必須是一個純函數
純函數遵守以下一些約束。

  • 不得改寫參數
  • 不能調用系統 I/O 的API
  • 不能調用Date.now()或者Math.random()等不純的方法,因為每次會得到不一樣的結果

步驟1:創建統一數據源

  1. 引入redux 生成createStore
  2. 創建默認數據 defaultState
  3. 創建 reduces(state,action){}
  4. createStore(reduces)創建數據源
//引入創建倉庫方法
import {createStore} from 'redux'
//默認數據源數據,不能直接修改
const defaultStore={
    count:1
}
//reducer出函數 
function reducers(state=defaultStore,action){
    if(action.type=='incr'){
        return{
            count:state.count+1
        }
    }
    return state
}
//創建唯一倉庫
const store = createStore(
    reducers,
)
export default store

步驟2:組件中獲取數據並設置數據

  1. 獲取數據 store.getState()
  2. 訂閱數據 store.subscribe(()=>{this.setState(state=>store.getState())})
  3. 派發任務 store.dispatch(actionCreator)
import React,{Component} from 'react'
import store from './store'
export default class App extends Component{
    constructor(props){
        super(props)
        this.state = store.getState()
        store.subscribe(()=>{
            this.setState(state=>store.getState())
            })
    }
    render(){
        return(
            <div>
                <button onClick={this.incr}>++</button>
            </div>
        )
    }
    incr=()=>{
        const actionCreator=>{
            type:'incr',
            payLoad:1
        }
        store.dispatch(actionCreator)
    }
}

划分reducer
原因:一個應用只有一個state,單個reducer對數據操作很臃腫,so需要按照不同功能去拆分

注意:

  1. 分離reducer的時候,每一個reducer維護的狀態都應該不同
  2. 通過store.getState獲取到的數據也是會按照reducers去划分的
  3. 划分多個reducer的時候,默認狀態只能創建在reducer中,因為划分reducer的目的,就是為了讓每一個reducer都去獨立管理一部分狀態

react-redux(redux擴展庫)

React-Redux是Redux的官方針對React開發的擴展庫。
你可以理解為react-redux就是redux給我們提供一些高階組件
安裝

npm i -S redux react-redux

兩個核心的api

  1. Provider: 提供store
    根據單一store原則 ,一般只會出現在整個應用程序的最頂層。
  2. connect: 用於連接容器組件和展示組件

    語法格式為:connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)(component)
    一般來說只會用到前面兩個,它的作用是:

    • store.getState() 轉化為展示組件的props
    • actionCreators 轉化為展示組件props上的方法

使用
步驟1:定義Provider

  • 主程序index.js中定義Provider
  • 讓全局的組件共享store中的數據
    import React from 'react'
    import ReactDOM from 'react-dom'
    import { Provider } from 'react-redux'
    import store from './store'
    import App from './App'
    ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root')
    )
    

步驟2:子程序中使用connect

  • store.getState() 轉化為展示組件的props
  • actionCreators 轉化為展示組件props上的方法
  1. 傳統使用方式
  2. 裝飾器使用方式(推薦使用)

傳統使用方式

import React ,{Component} from 'react'
import {connect} from 'react-redux'
import * as actions frm './countAction'
class App exteds Component{
    render(){
        return (
            <div>
                {this.props.count}
                <button onClick={this.incr}>++</button>
            </div>
        )
    }
    incr=()=>{
        this.props.incr()
    }
}
const mapStateProps=state=>{
    return {count:state.count}
}
const mapPropsToDIspatch=dispatch=>{
    return{
        incr(){
            dispatch(actions.incr())
        }
    }
}
export default connect(mapStateProps,mapPropsToDIspatch)(App)

裝飾器使用方式(推薦使用)

import React ,{Component} from 'react'
import {connect} from 'react-redux'
import * as actions frm './countAction'
const mapStateProps=state=>{
    return {count:state.count}
}
const mapPropsToDIspatch=dispatch=>{
    return{
        incr(){
            dispatch(actions.incr())
        }
    }
}
@connect(mapStateProps,mapPropsToDIspatch)
class App exteds Component{
    render(){
        return (
            <div>
                {this.props.count}
                <button onClick={this.incr}>++</button>
            </div>
        )
    }
    incr=()=>{
        this.props.incr()
    }
}

Redux異步操作(redux-thunk)

通常情況下,action只是一個對象,不能包含異步操作,這導致了很多創建action的邏輯只能寫在組件中,代碼量較多也不便於復用,同時對該部分代碼測試的時候也比較困難.
常見的異步庫:

  • Redux-thunk
  • Redux-saga
  • Redux-effects
  • Redux-side-effects
  • Redux-loop
  • Redux-observable

基於Promise的異步庫:

  • Redux-promise
  • Redux-promises
  • Redux-simple-promise
  • Redux-promise-middleware

安裝

npm i -S redux-thunk

使用
在createStore實例store中使用

import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import reducer from  './countReducer'
const store = createStore(
    reducer,
    applyMiddleware(thunk)
)
export default store

countReducer.js

const incrAction = num=>({
    type:'incr',
    payload:num
})
export const incr=>90=>dispatch=>{
    setTimeout(()=>{
        let num=10
        dispatch(incrAction(num))
    },1000)
} 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM