React各種狀態管理器的解讀及使用方法
首先我們要先知道什麼是狀態管理器,這玩意是幹啥的?
當我們在多個頁面中使用到瞭相同的屬性時就可以用到狀態管理器,將這些狀態存到外部的一個單獨的文件中,不管在什麼時候想使用都可以很方便的獲取。
react和vue有些不同,react沒有自己專屬的狀態管理方式。它使用的其實是js相關的狀態管理器。我們需要記住的是,視圖可以引起狀態的改變,而狀態的改變會導致視圖的二次渲染。
說瞭這麼半天,那麼我們在react中到底是怎樣使用狀態管理器的呢?
redux閃亮登場
redux的前身技術是flux,他和flux很相像,但是又不完全相同。兩者都規定將模型的更新邏輯全部集中在一個層面中(Flux之中的store,redux之中的reducer);但是redux中沒有dispatcher的概念,他依賴的是純函數來做事件處理器;並且redux不回去修改你的數據,它會返回一個新的對象用於更新state狀態。
首先我們先來認識一下redux中的一些屬性
1、store:
保存數據/狀態的地方,可以把它看成是一個容器。記得在整個應用之中隻能有一個store
import { createStore } from 'redux' const store = createStore(fn)
2、state:
可以把state看成是store的實例對象,他包含瞭狀態管理器中所有的狀態,是某個時間點所有數據/狀態的集合
const state = store.getState()
3、action
redux中最重要的屬性之一,唯一修改store種狀態的方式就是提交一個action;action是一個對象,具有type屬性,通常這個type屬性作為提交的關鍵key值
const action = { type: 'ADD_TODO', payload: 'Learc Redux' //這裡的payload是作為參數傳入的,可以不寫 }
4、store.dispatch():
提交action的唯一方式,我們就是通過這種方式將action提交到狀態管理器,不管後期使用瞭什麼其他的狀態管理其工具,最終都歸結與這裡。
store.dispatch({ type: 'ADD_TODO' })
5、reducer:
store在收到action之後,必須返回一個新的state,這樣view才會發生變化,而這個state的計算過程就叫做reducer
reducer是一個函數,他接受當前的state和action,返回一個全新的state
const reducer = (state = { count: 10 //給state中的屬性設置初始值,因為狀態管理器在每次刷新後都會清空,我們可以從本地獲取上次存儲的值作為初始值 }, action) => { switch (action.type) { case 'REDUCE': // 這裡可以看到我們根據action的type值作為key值進行查找,當dispatch的時候,就可以根據這些key值的判斷查找到要執行的操作 return { ...state, count: state.count - action.payload } //我們根據action的需要,在原有的state基礎上,返回瞭一個新的state對象,沒有修改原來的state case 'ADD': return { ...state, count: state.count + action.payload } default: return state; } } export default reducer
可以看到我們返回新的state對象的方式是通過ES6中的 … 語法,這種方式看起來是不是有點復雜,有點點low?那麼我們介紹一種新的方式,首先引入immutable組件
這種方式其實是js實現瞭一種深拷貝,將原來的state對象深拷貝瞭一份,這樣對新的state對象做出任何修改都不會影響到原來的state,是不是特別香!!!
yarn add immutable -S
使用方式:
import { Map } from 'immutable' //引入Map函數 const user = (state = Map({ //使用Map深拷貝瞭state isLogin: localStorage.getItem('isLogin') === 'true', token: localStorage.getItem('token') || '', adminname: localStorage.getItem('adminname') || '', role: localStorage.getItem('role') * 1 || 1 }), action) => { switch (action.type) { case 'CHANGE_LOGIN_STATE': return state.set('isLogin', action.payload) //set方式寫入 case 'CHANGE_TOKEN': return state.set('token', action.payload) case 'CHANGE_ADMIN_NAME': return state.set('adminname', action.payload) case 'CHANGE_ROLE': return state.set('role', action.payload) default: return state } } export default user
state => { return { adminname: state.getIn(['user', 'adminname']), // get方式獲取值, 參數是這種形式 //第一個參數的含義: user狀態管理器中的 //第二個參數含義: 名為adminname的狀態/屬性 } }
6、設計狀態管理器
首先根據模塊化開發的思想,我們可以在一個項目中設計多個種類狀態管理器,最後合並到一個裡面;例如,商品的狀態,用戶信息的狀態….
import { createStore, combineReducers } from "redux"; // 這個combineReducers方法就是模塊化開發的關鍵,它幫助我們把所有分模塊的狀態管理器合並到一起 import pro from './modules/pro' import app from './modules/app' const reducer = combineReducers({ //把分模塊創建的所有reducer都集中到這裡的reducer pro, app }) const store = createStore(reducer) export default store
幫助解讀階段
我們要知道一件事,當我們在某一個時間點希望獲取狀態或者修改狀態的時候,狀態管理器store會為我們生成一個state實例對象,我們可以對這個實例對象進行操作。state的變化會引起View視圖的改變,但是因為用戶們接觸不到state,隻能接觸到View,所以state的變化必然也必須是由View引起的!!!而action其實就是view 發出的一個通知,當用戶修改瞭view的時候,就會發出這個通知,告訴程序,state需要發生改變瞭。
//我們可以這樣理解,dispatch,action,store三者的關系可以看成是郵局,郵件,收信人
//我們想寄一封信(action),告訴朋友(收信人),我們找到工作瞭,需要通過郵局(dispatch)的幫助;當郵局幫我們把郵件送到瞭收件人的地方時,收件人就獲取到瞭我要傳遞的信息,也會做出響應的改變
//我們在調用dispatch的時候,通過type(key值)找到對應的郵件送到store
狀態管理器是如何使用的呢?
// 可以通過provider和connect在組件中對狀態管理器進行‘鏈接',鏈接成功之後就可以使用狀態管理器中的狀態和方法瞭 // /src/xxx/index.jsx import {connect} from 'react-redux' function App (props) { ... } export default connet(mapStateToProps, mapDispatchToProps)(App) // /index.js import {Provider} from 'react-redux' import App from './App.jsx' import store './store/index.js' ReactDom.render ( <React.StrictMode> <Provider store = { store }> <App /> </Provider> </React.StrickMode> ) //也可以使用到裝飾器的高階函數 @connect @withRouter //以往從狀態樹取出對應的數據,讓後通過props傳給組件使用通過react-redux自帶的connect()方法 class Home extends React.Component { //.... } export default connect(state => ({todos: state.todos}))(Home); //使用裝飾器的話就變成這樣,好像沒那麼復雜 @connect(state => ({ todos: state.todos })) class Home extends React.Component { //.... }
這裡我們對這種方式做出講解:
我們要鏈接狀態管理器,首先在整個項目的入口文件index.js中引入狀態store,通過Provider的方式將store作為參數傳遞給子組件,有點類似於祖先組件給後代組件傳值的方式
其次,我們要在使用狀態管理器的組件中通過connect這一個高階函數進行連接,該高階函數的原理是,傳入函數作為參數,返回另一個函數
mapStateToProps:
從名字可以看出,是把state中的狀態遍歷處理,放到props中,我們就可以在函數式組件中的props參數值裡面獲取到state.
mapDispatchToProps:
將狀態管理器中的提交方法存入到props中,這樣我們就可以在組件中對狀態管理器中的狀態進行修改。
const App = (props) => { // 組件中直接就可以通過props訪問到狀態管理器中的狀態 props.adminname props.count props.bannerList props.reduceFn ... } export default connect( // 可以看到這裡就是傳入兩個函數,返回兩個函數 state => { return { adminname: state.getIn(['user', 'adminname']), //這是一種存儲狀態的方式,一會會講到 count: state.app.count, //參數是state,我們把app狀態管理器中的count屬性傳遞到props中的count bannerList: state.pro.bannerList, } }, dispatch => { return { reduceFn () { //我們在這裡定義瞭一個reduceFn,裡面是dispatch的方法,我們在props中就可以通過reduceFn這個方法發送'REDUCE'提交的信息 dispatch({ type: 'REDUCE', payload: 5 //payload為參數,可以不傳 }) } } } )(App)
我們除瞭可以使用這種基本的方式修改狀態意外,還可以使用一些工具
redux-thunk、redux-saga
redux-thunk的使用
//在store.js之中把thunk引入並掛載到狀態管理器中 import { createStore, combineReducers, applyMiddleware} from 'redux' import thunk from 'redux-thunk' import app from './modules/app' import pro from './modules/pro' const reducer = combineReducers({ app, pro }) // 通過applyMiddleware將thunk掛載到狀態管理器 const store = createStore(reducer, applyMiddleware(thunk)) export default store
然後我們單獨設計一個文件用來封裝修改狀態的方式,包含異步方式
// .../store/actionCreator/pro.js // 這個文件就是專門用來觸發異步操作 // thunk模塊執行的時候, actionCreator 函數有默認的參數為dispatch // 該dispatch 可以用來觸發reducer // 有時候在觸發異步的時候, 需要傳遞參數,這個時候,可以在函數內部返回一個 actionCreator 函數 const actions = { getBannerListAction (dispatch) { fetch('http://121.89.205.189/api/banner/list') .then(res => res.json()) .then(res => { dispatch({ type: 'CHANGE_BANNER_LIST', payload: res.data }) }) }, getProListAction (count) { //有參數,返回一個函數,函數參數默認為dispatch count = count || 1 return function (dispatch) { fetch('http://121.89.205.189/api/pro/list?count=' + count) .then(res => res.json()) .then(res => { dispatch({ type: 'CHANGE_PRO_LIST', payload: res.data }) }) } } } export default actions
可以把上面的步驟看成定義瞭一個action的對象,裡面有一些提交action的dispatch,當我們要在組件中要修改狀態時,可以直接在這個對象中使用函數,函數會自動發起請求,提交action。
在下面組件中的使用也可以看出來,我們dispatch(actions.getBannerListAction);其實就是提交aciton的形式,隻不過我們把action修改和異步請求封裝起來瞭
import actions from './store/actionCreator/pro' const App = (props) => { // props之中可以直接訪問到 props.reduceFn() props.addFn() props.getBannerList() props.getProList() } const mapStateToProps = (state) => { return { count: state.app.count, bannerList: state.pro.bannerList, proList: state.pro.proList } } const mapDispatchToProps = (dispatch) => { return { reduceFn () { //通過正常方式修改狀態 dispatch({ type: 'REDUCE', payload: 5 }) }, addFn () { dispatch({ type: 'ADD', payload: 5 }) }, getBannerList () { //通過thunk方式修改狀態 dispatch(actions.getBannerListAction) }, getProList () { dispatch(actions.getProListAction(2)) } } } export default connect(mapStateToProps, mapDispatchToProps)(App)
鏈接的方式和普通的react-redux一模一樣,最後也是通過dispatch一個action的方式修改狀態
react-saga的使用
安裝redux-saga
yarn add redux-saga immutable redux-immutable -S
可以把redux-saga和redux-thunk看作是一種發送dispatch的方式,在舊時代我們送信(dispatch)是通過汽車、步行;使用工具可以看成是通過動車,飛機發送信件
import { createStore, combineReducers, applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' import mySaga from './mySaga' //異步操作說明 import home from './modules/home' import app from './modules/app' const reducer = combineReducers({ app, home }) const sagaMiddleware = createSagaMiddleware() //生成saga中間件 const store = createStore(reducer, applyMiddleware(sagaMiddleware)) //建立鏈接 //和thunk一樣,把saga中間件掛載到狀態管理器中就可以使用saga的方式修改狀態瞭 sagaMiddleware.run(mySaga) //run: 發送 // 這裡是封裝瞭一個mySage函數作為修改狀態的函數 export default store
接下來具體介紹saga如何修改狀態
在redux-saga中,修改狀態時使用Genarator函數實現的
import { call, put, takeLatest } from 'redux-saga/effects' import { getBannerList, getProList } from '../api/home' // redux-saga ---> 必須與generator函數一起使用 function * getBannerListAction() { const res = yield call(getBannerList) //call--調用函數 yield put({ type: 'CHANGE_BANNER_LIST', payload: res.data }) } function * getProListAction (action){ const res = yield call(getProList, action.payload) yield put({ type: 'CHANGE_PRO_LIST', payload: res.data }) } function * mySaga () { yield takeLatest('REQUEST_BANNER_LIST', getBannerListAction) yield takeLatest('REQUEST_PRO_LIST', getProListAction) } export default mySaga
如果看不懂上面,別怕。看這裡
// mysaga文件中我們定義瞭發送的方式 import { takeLatest } from 'redux-saga/effects' // takeLatest ---分配任務;在下方。我們自己定義瞭key並為其分配瞭事件,這些事件就是store.dispatch()函數使用的 function * getProListAction (action){ const res = yield call(getProList, action.payload) yield put({ type: 'CHANGE_PRO_LIST', payload: res.data }) } function * mySaga () { yield takeLatest('REQUEST_PRO_LIST', getProListAction) } // 我們以後再想修改狀態的時候就不需要使用store.dispatch()這樣修改瞭 // 可以使用這個文件中定義的key值進行修改 // 我們在組件的connect中這樣定義自定義函數,直接根據key值調用這裡的修改方法 dispatch => { dispatch({ type: 'REQUEST_PRO_LIST'}) } // put, call // call ---> 含義是調用 // put ---> 含義是推,把當前的action推到下一個去執行(隊列)。 yield put(action) yield call(fn)
以上就是本人結合各種文檔對於React常用的狀態管理器的一些理解,如果有說錯的地方,還希望大傢能指出,我們共同進步。
除瞭以上這些狀態管理器,市面上還有一些工具,MobX,Umi,Dva,這些有時間的話本人也會整理出來與大傢共享。
到此這篇關於對於React各種狀態管理器的解讀的文章就介紹到這瞭,更多相關React狀態管理器內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 使用react+redux實現計數器功能及遇到問題
- 詳解React 和 Redux的關系
- React Redux使用配置詳解
- 一文搞懂redux在react中的初步用法
- React 中使用 Redux 的 4 種寫法小結