一、引入:在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
一、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))