React各種狀態(tài)管理器的解讀及使用方法
首先我們要先知道什么是狀態(tài)管理器,這玩意是干啥的?
當(dāng)我們在多個頁面中使用到了相同的屬性時就可以用到狀態(tài)管理器,將這些狀態(tài)存到外部的一個單獨的文件中,不管在什么時候想使用都可以很方便的獲取。
react和vue有些不同,react沒有自己專屬的狀態(tài)管理方式。它使用的其實是js相關(guān)的狀態(tài)管理器。我們需要記住的是,視圖可以引起狀態(tài)的改變,而狀態(tài)的改變會導(dǎo)致視圖的二次渲染。
說了這么半天,那么我們在react中到底是怎樣使用狀態(tài)管理器的呢?
redux閃亮登場
redux的前身技術(shù)是flux,他和flux很相像,但是又不完全相同。兩者都規(guī)定將模型的更新邏輯全部集中在一個層面中(Flux之中的store,redux之中的reducer);但是redux中沒有dispatcher的概念,他依賴的是純函數(shù)來做事件處理器;并且redux不回去修改你的數(shù)據(jù),它會返回一個新的對象用于更新state狀態(tài)。
首先我們先來認(rèn)識一下redux中的一些屬性
1、store:
保存數(shù)據(jù)/狀態(tài)的地方,可以把它看成是一個容器。記得在整個應(yīng)用之中只能有一個store
import { createStore } from 'redux'
const store = createStore(fn)
2、state:
可以把state看成是store的實例對象,他包含了狀態(tài)管理器中所有的狀態(tài),是某個時間點所有數(shù)據(jù)/狀態(tài)的集合
const state = store.getState()
3、action
redux中最重要的屬性之一,唯一修改store種狀態(tài)的方式就是提交一個action;action是一個對象,具有type屬性,通常這個type屬性作為提交的關(guān)鍵key值
const action = {
type: 'ADD_TODO',
payload: 'Learc Redux' //這里的payload是作為參數(shù)傳入的,可以不寫
}
4、store.dispatch():
提交action的唯一方式,我們就是通過這種方式將action提交到狀態(tài)管理器,不管后期使用了什么其他的狀態(tài)管理其工具,最終都?xì)w結(jié)與這里。
store.dispatch({
type: 'ADD_TODO'
})
5、reducer:
store在收到action之后,必須返回一個新的state,這樣view才會發(fā)生變化,而這個state的計算過程就叫做reducer
reducer是一個函數(shù),他接受當(dāng)前的state和action,返回一個全新的state
const reducer = (state = {
count: 10 //給state中的屬性設(shè)置初始值,因為狀態(tài)管理器在每次刷新后都會清空,我們可以從本地獲取上次存儲的值作為初始值
}, action) => {
switch (action.type) {
case 'REDUCE':
// 這里可以看到我們根據(jù)action的type值作為key值進(jìn)行查找,當(dāng)dispatch的時候,就可以根據(jù)這些key值的判斷查找到要執(zhí)行的操作
return { ...state, count: state.count - action.payload }
//我們根據(jù)action的需要,在原有的state基礎(chǔ)上,返回了一個新的state對象,沒有修改原來的state
case 'ADD':
return { ...state, count: state.count + action.payload }
default:
return state;
}
}
export default reducer
可以看到我們返回新的state對象的方式是通過ES6中的 ... 語法,這種方式看起來是不是有點復(fù)雜,有點點low?那么我們介紹一種新的方式,首先引入immutable組件
這種方式其實是js實現(xiàn)了一種深拷貝,將原來的state對象深拷貝了一份,這樣對新的state對象做出任何修改都不會影響到原來的state,是不是特別香?。?!
yarn add immutable -S
使用方式:
import { Map } from 'immutable' //引入Map函數(shù)
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方式獲取值, 參數(shù)是這種形式
//第一個參數(shù)的含義: user狀態(tài)管理器中的
//第二個參數(shù)含義: 名為adminname的狀態(tài)/屬性
}
}
6、設(shè)計狀態(tài)管理器
首先根據(jù)模塊化開發(fā)的思想,我們可以在一個項目中設(shè)計多個種類狀態(tài)管理器,最后合并到一個里面;例如,商品的狀態(tài),用戶信息的狀態(tài)....
import { createStore, combineReducers } from "redux";
// 這個combineReducers方法就是模塊化開發(fā)的關(guān)鍵,它幫助我們把所有分模塊的狀態(tài)管理器合并到一起
import pro from './modules/pro'
import app from './modules/app'
const reducer = combineReducers({ //把分模塊創(chuàng)建的所有reducer都集中到這里的reducer
pro, app
})
const store = createStore(reducer)
export default store
幫助解讀階段
我們要知道一件事,當(dāng)我們在某一個時間點希望獲取狀態(tài)或者修改狀態(tài)的時候,狀態(tài)管理器store會為我們生成一個state實例對象,我們可以對這個實例對象進(jìn)行操作。state的變化會引起View視圖的改變,但是因為用戶們接觸不到state,只能接觸到View,所以state的變化必然也必須是由View引起的?。?!而action其實就是view 發(fā)出的一個通知,當(dāng)用戶修改了view的時候,就會發(fā)出這個通知,告訴程序,state需要發(fā)生改變了。
//我們可以這樣理解,dispatch,action,store三者的關(guān)系可以看成是郵局,郵件,收信人
//我們想寄一封信(action),告訴朋友(收信人),我們找到工作了,需要通過郵局(dispatch)的幫助;當(dāng)郵局幫我們把郵件送到了收件人的地方時,收件人就獲取到了我要傳遞的信息,也會做出響應(yīng)的改變
//我們在調(diào)用dispatch的時候,通過type(key值)找到對應(yīng)的郵件送到store
狀態(tài)管理器是如何使用的呢?
// 可以通過provider和connect在組件中對狀態(tài)管理器進(jìn)行‘鏈接',鏈接成功之后就可以使用狀態(tài)管理器中的狀態(tài)和方法了
// /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>
)
//也可以使用到裝飾器的高階函數(shù) @connect @withRouter
//以往從狀態(tài)樹取出對應(yīng)的數(shù)據(jù),讓后通過props傳給組件使用通過react-redux自帶的connect()方法
class Home extends React.Component {
//....
}
export default connect(state => ({todos: state.todos}))(Home);
//使用裝飾器的話就變成這樣,好像沒那么復(fù)雜
@connect(state => ({ todos: state.todos }))
class Home extends React.Component {
//....
}
這里我們對這種方式做出講解:
我們要鏈接狀態(tài)管理器,首先在整個項目的入口文件index.js中引入狀態(tài)store,通過Provider的方式將store作為參數(shù)傳遞給子組件,有點類似于祖先組件給后代組件傳值的方式
其次,我們要在使用狀態(tài)管理器的組件中通過connect這一個高階函數(shù)進(jìn)行連接,該高階函數(shù)的原理是,傳入函數(shù)作為參數(shù),返回另一個函數(shù)
mapStateToProps:
從名字可以看出,是把state中的狀態(tài)遍歷處理,放到props中,我們就可以在函數(shù)式組件中的props參數(shù)值里面獲取到state.
mapDispatchToProps:
將狀態(tài)管理器中的提交方法存入到props中,這樣我們就可以在組件中對狀態(tài)管理器中的狀態(tài)進(jìn)行修改。
const App = (props) => {
// 組件中直接就可以通過props訪問到狀態(tài)管理器中的狀態(tài)
props.adminname
props.count
props.bannerList
props.reduceFn
...
}
export default connect(
// 可以看到這里就是傳入兩個函數(shù),返回兩個函數(shù)
state => {
return {
adminname: state.getIn(['user', 'adminname']), //這是一種存儲狀態(tài)的方式,一會會講到
count: state.app.count, //參數(shù)是state,我們把app狀態(tài)管理器中的count屬性傳遞到props中的count
bannerList: state.pro.bannerList,
}
},
dispatch => {
return {
reduceFn () { //我們在這里定義了一個reduceFn,里面是dispatch的方法,我們在props中就可以通過reduceFn這個方法發(fā)送'REDUCE'提交的信息
dispatch({
type: 'REDUCE',
payload: 5 //payload為參數(shù),可以不傳
})
}
}
}
)(App)
我們除了可以使用這種基本的方式修改狀態(tài)意外,還可以使用一些工具
redux-thunk、redux-saga
redux-thunk的使用
//在store.js之中把thunk引入并掛載到狀態(tài)管理器中
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掛載到狀態(tài)管理器
const store = createStore(reducer, applyMiddleware(thunk))
export default store
然后我們單獨設(shè)計一個文件用來封裝修改狀態(tài)的方式,包含異步方式
// .../store/actionCreator/pro.js
// 這個文件就是專門用來觸發(fā)異步操作
// thunk模塊執(zhí)行的時候, actionCreator 函數(shù)有默認(rèn)的參數(shù)為dispatch
// 該dispatch 可以用來觸發(fā)reducer
// 有時候在觸發(fā)異步的時候, 需要傳遞參數(shù),這個時候,可以在函數(shù)內(nèi)部返回一個 actionCreator 函數(shù)
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) { //有參數(shù),返回一個函數(shù),函數(shù)參數(shù)默認(rèn)為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,當(dāng)我們要在組件中要修改狀態(tài)時,可以直接在這個對象中使用函數(shù),函數(shù)會自動發(fā)起請求,提交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 () { //通過正常方式修改狀態(tài)
dispatch({
type: 'REDUCE',
payload: 5
})
},
addFn () {
dispatch({
type: 'ADD',
payload: 5
})
},
getBannerList () { //通過thunk方式修改狀態(tài)
dispatch(actions.getBannerListAction)
},
getProList () {
dispatch(actions.getProListAction(2))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
鏈接的方式和普通的react-redux一模一樣,最后也是通過dispatch一個action的方式修改狀態(tài)
react-saga的使用
安裝redux-saga
yarn add redux-saga immutable redux-immutable -S
可以把redux-saga和redux-thunk看作是一種發(fā)送dispatch的方式,在舊時代我們送信(dispatch)是通過汽車、步行;使用工具可以看成是通過動車,飛機(jī)發(fā)送信件
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中間件掛載到狀態(tài)管理器中就可以使用saga的方式修改狀態(tài)了
sagaMiddleware.run(mySaga)
//run: 發(fā)送
// 這里是封裝了一個mySage函數(shù)作為修改狀態(tài)的函數(shù)
export default store
接下來具體介紹saga如何修改狀態(tài)
在redux-saga中,修改狀態(tài)時使用Genarator函數(shù)實現(xiàn)的
import { call, put, takeLatest } from 'redux-saga/effects'
import { getBannerList, getProList } from '../api/home'
// redux-saga ---> 必須與generator函數(shù)一起使用
function * getBannerListAction() {
const res = yield call(getBannerList) //call--調(diào)用函數(shù)
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文件中我們定義了發(fā)送的方式
import { takeLatest } from 'redux-saga/effects'
// takeLatest ---分配任務(wù);在下方。我們自己定義了key并為其分配了事件,這些事件就是store.dispatch()函數(shù)使用的
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)
}
// 我們以后再想修改狀態(tài)的時候就不需要使用store.dispatch()這樣修改了
// 可以使用這個文件中定義的key值進(jìn)行修改
// 我們在組件的connect中這樣定義自定義函數(shù),直接根據(jù)key值調(diào)用這里的修改方法
dispatch => {
dispatch({ type: 'REQUEST_PRO_LIST'})
}
// put, call
// call ---> 含義是調(diào)用
// put ---> 含義是推,把當(dāng)前的action推到下一個去執(zhí)行(隊列)。
yield put(action)
yield call(fn)
以上就是本人結(jié)合各種文檔對于React常用的狀態(tài)管理器的一些理解,如果有說錯的地方,還希望大家能指出,我們共同進(jìn)步。
除了以上這些狀態(tài)管理器,市面上還有一些工具,MobX,Umi,Dva,這些有時間的話本人也會整理出來與大家共享。
到此這篇關(guān)于對于React各種狀態(tài)管理器的解讀的文章就介紹到這了,更多相關(guān)React狀態(tài)管理器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React-Native做一個文本輸入框組件的實現(xiàn)代碼
這篇文章主要介紹了React-Native做一個文本輸入框組件的實現(xiàn)代碼,非常具有實用價值,需要的朋友可以參考下2017-08-08
深入理解React調(diào)度(Scheduler)原理
本文主要介紹了深入理解React調(diào)度(Scheduler)原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
React Native可復(fù)用 UI分離布局組件和狀態(tài)組件技巧
這篇文章主要為大家介紹了React Native可復(fù)用 UI分離布局組件和狀態(tài)組件使用技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09

