1. 程式人生 > 程式設計 >React各種狀態管理器的解讀及使用方法

React各種狀態管理器的解讀及使用方法

首先我們要先知道什麼是狀態管理器,這玩意是幹啥的?

當我們在多個頁面中使用到了相同的屬性時就可以用到狀態管理器,將這些狀態存到外部的一個單獨的檔案中,不管在什麼時候想使用都可以很方便的獲取。

react和有些不同,react沒有自己專屬的狀態管理方式。它使用的其實是相關的狀態管理器。我們需要記住的是,檢視可以引起狀態的改變,而狀態的改變會導致檢視的二次渲染。

說了這麼半天,那麼我們在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('roleYIgds',action.payload)
    default:
      return state
  }
}

export default user
www.cppcns.comstate => {
    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 = combineReducerhttp://www.cppcns.coms({   //把分模組建立的所有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',//這是一種儲存狀態的方式,一會會講到
            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',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,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狀態管理器內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!