溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

react中間件有什么用

發(fā)布時(shí)間:2020-12-01 09:45:49 來源:億速云 閱讀:275 作者:小新 欄目:web開發(fā)

小編給大家分享一下react中間件有什么用,希望大家閱讀完這篇文章后大所收獲,下面讓我們一起去探討吧!

在react中,中間件就是一個(gè)函數(shù),對store.dispatch方法進(jìn)行了改造,在發(fā)出Action和執(zhí)行Reducer這兩步之間,添加了其他功能;常用的中間件都有現(xiàn)成的,只要引用別人寫好的模塊即可。

一、中間件的概念

為了理解中間件,讓我們站在框架作者的角度思考問題:如果要添加功能,你會(huì)在哪個(gè)環(huán)節(jié)添加?

(1)Reducer:純函數(shù),只承擔(dān)計(jì)算 State 的功能,不合適承擔(dān)其他功能,也承擔(dān)不了,因?yàn)槔碚撋希兒瘮?shù)不能進(jìn)行讀寫操作。

(2)View:與 State 一一對應(yīng),可以看作 State 的視覺層,也不合適承擔(dān)其他功能。

(3)Action:存放數(shù)據(jù)的對象,即消息的載體,只能被別人操作,自己不能進(jìn)行任何操作。

想來想去,只有發(fā)送 Action 的這個(gè)步驟,即store.dispatch()方法,可以添加功能。舉例來說,要添加日志功能,把 Action 和 State 打印出來,可以對store.dispatch進(jìn)行如下改造。

let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
  console.log('dispatching', action);
  next(action);
  console.log('next state', store.getState());
}

上面代碼中,對store.dispatch進(jìn)行了重定義,在發(fā)送 Action 前后添加了打印功能。這就是中間件的雛形。

中間件就是一個(gè)函數(shù),對store.dispatch方法進(jìn)行了改造,在發(fā)出 Action 和執(zhí)行 Reducer 這兩步之間,添加了其他功能。常用的中間件都有現(xiàn)成的,只要引用別人寫好的模塊即可。

二、中間件的用法

本教程不涉及如何編寫中間件,因?yàn)槌S玫闹虚g件都有現(xiàn)成的,只要引用別人寫好的模塊即可。比如,上一節(jié)的日志中間件,就有現(xiàn)成的redux-logger模塊。這里只介紹怎么使用中間件。

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();
 
const store = createStore(
  reducer,
  applyMiddleware(logger)
);

上面代碼中,redux-logger提供一個(gè)生成器createLogger,可以生成日志中間件logger。然后,將它放在applyMiddleware方法之中,傳入createStore方法,就完成了store.dispatch()的功能增強(qiáng)。

這里有兩點(diǎn)需要注意:

(1)createStore方法可以接受整個(gè)應(yīng)用的初始狀態(tài)作為參數(shù),那樣的話,applyMiddleware就是第三個(gè)參數(shù)了。

const store = createStore(
  reducer,
  initial_state,
  applyMiddleware(logger)
);

(2)中間件的次序有講究。

const store = createStore(
  reducer,
  applyMiddleware(thunk, promise, logger)
);

上面代碼中,applyMiddleware方法的三個(gè)參數(shù),就是三個(gè)中間件。有的中間件有次序要求,使用前要查一下文檔。比如,logger就一定要放在最后,否則輸出結(jié)果會(huì)不正確。

三、applyMiddlewares()

看到這里,你可能會(huì)問,applyMiddlewares這個(gè)方法到底是干什么的?

它是 Redux 的原生方法,作用是將所有中間件組成一個(gè)數(shù)組,依次執(zhí)行。下面是它的源碼。

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer);
    var dispatch = store.dispatch;
    var chain = [];
 
    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    };
    chain = middlewares.map(middleware => middleware(middlewareAPI));
    dispatch = compose(...chain)(store.dispatch);
 
    return {...store, dispatch}
  }
}

上面代碼中,所有中間件被放進(jìn)了一個(gè)數(shù)組chain,然后嵌套執(zhí)行,最后執(zhí)行store.dispatch。可以看到,中間件內(nèi)部(middlewareAPI)可以拿到getState和dispatch這兩個(gè)方法。

四、異步操作的基本思路

理解了中間件以后,就可以處理異步操作了。

同步操作只要發(fā)出一種 Action 即可,異步操作的差別是它要發(fā)出三種 Action。

  • 操作發(fā)起時(shí)的 Action

  • 操作成功時(shí)的 Action

  • 操作失敗時(shí)的 Action

以向服務(wù)器取出數(shù)據(jù)為例,三種 Action 可以有兩種不同的寫法。

// 寫法一:名稱相同,參數(shù)不同
{ type: 'FETCH_POSTS' }
{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
{ type: 'FETCH_POSTS', status: 'success', response: { ... } }
// 寫法二:名稱不同
{ type: 'FETCH_POSTS_REQUEST' }
{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }

除了 Action 種類不同,異步操作的 State 也要進(jìn)行改造,反映不同的操作狀態(tài)。下面是 State 的一個(gè)例子。

let state = {
  // ... 
  isFetching: true,
  didInvalidate: true,
  lastUpdated: 'xxxxxxx'
};

上面代碼中,State 的屬性isFetching表示是否在抓取數(shù)據(jù)。didInvalidate表示數(shù)據(jù)是否過時(shí),lastUpdated表示上一次更新時(shí)間。

現(xiàn)在,整個(gè)異步操作的思路就很清楚了。

  • 操作開始時(shí),送出一個(gè) Action,觸發(fā) State 更新為"正在操作"狀態(tài),View 重新渲染

  • 操作結(jié)束后,再送出一個(gè) Action,觸發(fā) State 更新為"操作結(jié)束"狀態(tài),View 再一次重新渲染

五、redux-thunk 中間件

異步操作至少要送出兩個(gè) Action:用戶觸發(fā)第一個(gè) Action,這個(gè)跟同步操作一樣,沒有問題;如何才能在操作結(jié)束時(shí),系統(tǒng)自動(dòng)送出第二個(gè) Action 呢?

奧妙就在 Action Creator 之中。

class AsyncApp extends Component {
  componentDidMount() {
    const { dispatch, selectedPost } = this.props
    dispatch(fetchPosts(selectedPost))
  }
// ...

上面代碼是一個(gè)異步組件的例子。加載成功后(componentDidMount方法),它送出了(dispatch方法)一個(gè) Action,向服務(wù)器要求數(shù)據(jù) fetchPosts(selectedSubreddit)。這里的fetchPosts就是 Action Creator。

下面就是fetchPosts的代碼,關(guān)鍵之處就在里面。

react中間件有什么用

const fetchPosts = postTitle => (dispatch, getState) => {
  dispatch(requestPosts(postTitle));
  return fetch(`/some/API/${postTitle}.json`)
    .then(response => response.json())
    .then(json => dispatch(receivePosts(postTitle, json)));
  };
};
// 使用方法一
store.dispatch(fetchPosts('reactjs'));
// 使用方法二
store.dispatch(fetchPosts('reactjs')).then(() =>
  console.log(store.getState())
);

上面代碼中,fetchPosts是一個(gè)Action Creator(動(dòng)作生成器),返回一個(gè)函數(shù)。這個(gè)函數(shù)執(zhí)行后,先發(fā)出一個(gè)Action(requestPosts(postTitle)),然后進(jìn)行異步操作。拿到結(jié)果后,先將結(jié)果轉(zhuǎn)成 JSON 格式,然后再發(fā)出一個(gè) Action( receivePosts(postTitle, json))。

上面代碼中,有幾個(gè)地方需要注意。

(1)fetchPosts返回了一個(gè)函數(shù),而普通的 Action Creator 默認(rèn)返回一個(gè)對象。

(2)返回的函數(shù)的參數(shù)是dispatch和getState這兩個(gè) Redux 方法,普通的 Action Creator 的參數(shù)是 Action 的內(nèi)容。

(3)在返回的函數(shù)之中,先發(fā)出一個(gè) Action(requestPosts(postTitle)),表示操作開始。

(4)異步操作結(jié)束之后,再發(fā)出一個(gè) Action(receivePosts(postTitle, json)),表示操作結(jié)束。

這樣的處理,就解決了自動(dòng)發(fā)送第二個(gè) Action 的問題。但是,又帶來了一個(gè)新的問題,Action 是由store.dispatch方法發(fā)送的。而store.dispatch方法正常情況下,參數(shù)只能是對象,不能是函數(shù)。

這時(shí),就要使用中間件redux-thunk。

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';
// Note: this API requires redux@>=3.1.0
const store = createStore(
  reducer,
  applyMiddleware(thunk)
);

上面代碼使用redux-thunk中間件,改造store.dispatch,使得后者可以接受函數(shù)作為參數(shù)。

因此,異步操作的第一種解決方案就是,寫出一個(gè)返回函數(shù)的 Action Creator,然后使用redux-thunk中間件改造store.dispatch。

六、redux-promise 中間件

既然 Action Creator 可以返回函數(shù),當(dāng)然也可以返回其他值。另一種異步操作的解決方案,就是讓 Action Creator 返回一個(gè) Promise 對象。

這就需要使用redux-promise中間件。

import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import reducer from './reducers';
 
const store = createStore(
  reducer,
  applyMiddleware(promiseMiddleware)
);

這個(gè)中間件使得store.dispatch方法可以接受 Promise 對象作為參數(shù)。這時(shí),Action Creator 有兩種寫法。寫法一,返回值是一個(gè) Promise 對象。

const fetchPosts = 
  (dispatch, postTitle) => new Promise(function (resolve, reject) {
     dispatch(requestPosts(postTitle));
     return fetch(`/some/API/${postTitle}.json`)
       .then(response => {
         type: 'FETCH_POSTS',
         payload: response.json()
       });
});

寫法二,Action 對象的payload屬性是一個(gè) Promise 對象。這需要從redux-actions模塊引入createAction方法,并且寫法也要變成下面這樣。

import { createAction } from 'redux-actions';
 
class AsyncApp extends Component {
  componentDidMount() {
    const { dispatch, selectedPost } = this.props
    // 發(fā)出同步 Action
    dispatch(requestPosts(selectedPost));
    // 發(fā)出異步 Action
    dispatch(createAction(
      'FETCH_POSTS', 
      fetch(`/some/API/${postTitle}.json`)
        .then(response => response.json())
    ));
  }

上面代碼中,第二個(gè)dispatch方法發(fā)出的是異步 Action,只有等到操作結(jié)束,這個(gè) Action 才會(huì)實(shí)際發(fā)出。注意,createAction的第二個(gè)參數(shù)必須是一個(gè) Promise 對象。

看一下redux-promise的源碼,就會(huì)明白它內(nèi)部是怎么操作的。

export default function promiseMiddleware({ dispatch }) {
  return next => action => {
    if (!isFSA(action)) {
      return isPromise(action)
        ? action.then(dispatch)
        : next(action);
    }
 
    return isPromise(action.payload)
      ? action.payload.then(
          result => dispatch({ ...action, payload: result }),
          error => {
            dispatch({ ...action, payload: error, error: true });
            return Promise.reject(error);
          }
        )
      : next(action);
  };
}

從上面代碼可以看出,如果 Action 本身是一個(gè) Promise,它 resolve 以后的值應(yīng)該是一個(gè) Action 對象,會(huì)被dispatch方法送出(action.then(dispatch)),但 reject 以后不會(huì)有任何動(dòng)作;如果 Action 對象的payload屬性是一個(gè) Promise 對象,那么無論 resolve 和 reject,dispatch方法都會(huì)發(fā)出 Action。

看完了這篇文章,相信你對react中間件有什么用有了一定的了解,想了解更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI