React初識整理(五)--Redux和Flux(解決狀態傳遞問題)

Flux

一、引入:在React的應⽤中,狀態管理是⼀個⾮常重要的⼯做。咱們不會直接對DOM節點進⾏操做,⽽是經過將數據設置給state,由state來同步UI,這種⽅式有個潛在的問題,每一個組件都有獨⽴的state,而且不能相互傳遞。若是從⼀個組件將數據傳遞給另⼀個組件,須要經過props。⽽props的特色是⾃頂⽽下的傳遞,那麼⼦組件要傳遞給⽗組件就會⽐較麻煩。當這種需求愈來愈多後,狀態管理就會變得更加困難。react

 

二、定義:它不是⼀種⼯具或框架,⽽是⼀種架構模式。它把全部的數據都集中放在了⼀個叫store的對象中。之後每⼀個組件都不在直接操做state,⽽是把更改數據的命令封裝成action,而後dispatch給store。 store再完成state的更改,對應的組件 完成UI更新。ios

三、核心概念:ajax

   - store:至關於數據中⼼,能夠有多個npm

   - action:操做命令redux

   - dispatch:分發action的對象axios

   - view:視圖組件服務器

  - 過程:view -> action -> dispatch -> store -> view antd

Redux

一、Flux的實現,也擴展了自身。它能夠用於任何組件化開發中,比較常見用於React中。架構

  ①核心概念:app

     - store:數據中⼼,只有⼀個

     - action:操做命令 - action creator:建立命令的⽅法

     - dispatch:分發action的對象 - middleware:中間件

     - reducer:更新state數據的⽅法

     - view:視圖

   ②流程:view -> action creator -> action -> dispatch -> middleware -> reducer -> -> state -> view

   ③reducer必須是1個純函數,就是函數裏的數據沒有反作用的,每調用1次函數的結果必須是一致的,且只能操做state,好比函數裏作加法,那麼就只能作加法,而不能一下子加法一下子減法。若是函數裏有異步操做,就會改變函數的操做方法,這樣就不會純函數。

二、具體用法: 安裝: npm i redux npm i react-redux

  ①編寫action creator。 在src下建立actions文件夾,actions下有index.js,用於儲存全部的action操做命令。

/src/actions/index.js
 export const setVisible = (visible) => {
    return {
        // type必須寫,是命令的名稱,必須全大寫,中間用_鏈接
        type: 'ADD_TODO',  
        visible
    }
}

  ②編寫reducer:在src文件夾下建立reducers文件夾,而後在此文件下創建index.js,用於將全部的更新state數據的方法reducer都合併到一塊兒,而後建立1個componentReducer.js文件存放單一的1個reducer。 將傳送過來的值設置到store的state裏:

// /src/reducers/componentReducer.js
// 傳入第一個參數state,剛開始可能什麼都沒有,因此給個默認初始值
// 傳入第二個參數action,即命令,它包含了傳過來的type和state的值。1個命令只改1個屬性

export default (state = { visible: false }, action) => {
    switch (action.type) {
            
        // 咱們不直接修改state的值,而是返回1個新的對象,這樣在作優化時即使淺比較也會更新。
        // 淺比較:賦對象的變量儲存的是地址,當咱們改屬性的值時,變量儲存的地址沒變,這樣淺比較時就認爲它是沒變化的,從而在優化時不會更新後續組件
        // 返回用ES6的擴展運算符,對象裏只能有惟一鍵,相同的會替換。
        
        case "SET_VISIBLE":
            return {...state,visible:action.visible}
        default:
            return state;
    }
}

  將多個reducer合併爲一個:

// /src/reducers/index.js
// 將全部的更新state數據的方法reducer都合併到一塊兒
import { combineReducers } from 'redux';
import componentReducer from './componentReducer'
import updateStudentReducer from './updateStudentReducer'
export default combineReducers({
    componentReducer,
    updateStudentReducer
})

  ③將reducer組合到store中:用到react-redux的Provider組件,將其設置成根組件,而後將store設置給它。

// /src/store/index.js
// store主要把reducer結合進來

import React from 'react';
import reducers from '../reducers';
import { Provider } from 'react-redux'
// 建立store
import { createStore} from 'redux'
let store = createStore(reducers)

// ⽤到react-redux的Provider組件,將其設置成根組件,而後將store設置給它
export default (props) => {
    return <Provider store={store}>
        {/* 將入口的組件傳進來*/}
        {props.children}
    </Provider>
}

咱們在src下創建了index.js,做爲項目的主入口,而後用< Store>將入口裝起來,做爲全部子組件的根級元素。

// /src/index.js
import React from "react"
import ReactDOM from "react-dom"
import Store from "./antd/store"
import Router from "./router"
// 把store做爲全部組件的根組件
ReactDOM.render(<Store><Router /></Store>, document.getElementById("root"));

到這裏,咱們就把基礎配置完成,下面就是如何使用它了。

 

  ④將組件和redux關聯起來:咱們先把子組件和redux關聯起來,也就是將子組件和store創建聯繫。在這裏咱們使用react-redux下的connect方法

import { connect } from "react-redux"
class StudentsList extends Component {
    //省略
}
// 關聯當前組件StudentsList到store後默認導出。關聯以後props裏纔有dispatch方法
export default connect()(StudentsList)

connect()()方法後置兩個圓括號, - 第一個圓括號用於映射,圓括號裏能夠是一個回調函數,自帶1個參數store,它存儲着全部的state狀態,咱們能夠直接調用。映射成功了的子組件中,就能夠直接經過 props來獲取state裏的值了。如:this.props.visible就能夠得到我存儲在store裏的visible的值 - 第二個圓括號用於關聯,圓括號裏寫1個要關聯的組件名。即將子組件關聯到store上。關聯以後子組件中的props才能dispatch方法,用於操做命令的分發。如:

import { setVisible } from "../actions"
  // 使用dispatch分發action對象到setVisible,並設置值爲true,這樣就調用了store裏面的actions操做命令裏的setVisible命令
this.props.dispatch(setVisible(true))

這樣咱們就能夠經過dispatch來分發action命令,從而調用以前咱們寫的命令setVisible來改變對應state的狀態了。

  ⑤異步操做存在問題及解決

  在⼀般狀況下,都是發出action後,由reducer完成state的計算,而後更新組件。可是若是遇到 有異步操做怎麼辦呢?reducer是純函數,不適合作除設置state之外的其餘操做。 action creator的⽅法要求返回的是⼀個命令對象,異步操做在這⾥也有問題。 咱們來看1個例子:

// /src/actions/index.js
// 當要發送請求時,由於axios是個異步操做,它會先return,那麼返回的student裏就沒有值,全部在這裏要引入中間件thunk
import axios from "axios";
export const setStudent = (id) => {
    let student;
    axios({
        method:"get",
        url:"/students/"+id
    }).then(({data})=>{
        student = data
    })
    return {
        type: 'SET_VISIBLE',  
        student
    }
}

  我用axios發送ajax向服務器請求數據,因爲ajax是異步操做,那麼return會比student = data(將取回的數據賦值給student)先執行,結果咱們就return的student結果只能爲undefined。return放到then方法裏面也不行,若是放裏面就沒法說清return是針對哪一個地方的了。那麼怎麼解決呢?

   redux提供了中間件來解決這個問題。咱們引入中間件:redux-thunk, 首先咱們將前面的store下的index.js進行修改:

// store主要把reducer結合進來

import React from 'react';
import reducers from '../reducers';
import { Provider } from 'react-redux'

// 引入異步操做的中間插件redux-thunk。安裝:npm i redux-thunk
import thunk from 'redux-thunk'
// 建立store,而後用 applyMiddleware來應用中間插件
import { createStore, applyMiddleware } from 'redux'
let store = createStore(reducers, applyMiddleware(logger,thunk))

// ⽤到react-redux的Provider組件,將其設置成根組件,而後將store設置給它
export default (props) => {
    return <Provider store={store}>
        {/* 將入口的傳進來 */}
        {props.children}
    </Provider>
}

在這裏,引入了中間件thunk,並將這個中間件應用到了reducers裏面。 而後咱們調用axios來發送異步請求:

// /src/actions/index.js
import axios from "axios";
export const setStudent = (id) => {
    return (dispatch, getState) => {
        axios({
            method: "get",
            url:"/students/"+id
        }).then(({ data }) => {
        //應用中間件後,這裏可使用dispatch來發送action命令更新數據
            dispatch({
                type: 'SET_STUDENT',
                updateStudent: data
            });
        })
    }
}

獲得數據後,咱們在須要用到的組件裏進行映射:

// /src/students/updateStudents.js
export default connect(
    // 簡化代碼 ,箭頭函數中的圓括號()就是return的意思
    // ({ componentReducer: { visible } }) => ( visible )
    // 等同於:
    (store) => {
        return {
            visible: store.componentReducer.visible,
            updateStudent:store.updateStudentReducer.updateStudent
        }}
)(
    Form.create({
        // antd裏的解決受控組件的方法
        //在這裏咱們就用上面映射回來的值設置到對應的輸入框了。
        mapPropsToFields(props) {
            return {
                name: Form.createFormField({
                    value: props.updateStudent.name
                }),
                age: Form.createFormField({
                    value: props.updateStudent.age
                }),
                gender: Form.createFormField({
                    value: props.updateStudent.gender
                }),
            };
        }
    })(UpdateStudents)
)

  ⑥在這裏補充1個使用的中間件logger

logger是一個日誌處理的中間件,它會自動打印操做時間的先後值的變化。

其中 prev state的內容爲操做前的內容, next state爲操做後的內容,這樣方便對比。

下載: npm i redux-logger.

使用:

import    {createLogger}    from 'redux-logger';
const    logger    =    createLogger();
let    store    =    createStore(reducers,applyMiddleware(logger))
相關文章
相關標籤/搜索