ホームページ  >  記事  >  ウェブフロントエンド  >  反応ミドルウェアとは何ですか?

反応ミドルウェアとは何ですか?

青灯夜游
青灯夜游オリジナル
2020-11-26 17:16:015279ブラウズ

reactではミドルウェアが関数になっており、store.dispatchメソッドが修正されています。Action発行とReducer実行の間にその他の関数が追加されており、よく使われるミドルウェアが用意されています。モジュールを参照するだけです。他の人が書いたもの。

反応ミドルウェアとは何ですか?

#1. ミドルウェアの概念

ミドルウェアを理解するために、ミドルウェアの観点から考えてみましょう。フレームワーク作成者 質問: 機能を追加するとしたら、どこに追加しますか?

(1) Reducer: 状態を計算する機能のみを実行する純粋関数。理論的に純粋関数は読み取りおよび書き込み操作を実行できないため、他の関数には適しておらず、実行できません。

(2) View: State と 1 対 1 に対応し、State の視覚的な層とみなすことができ、他の機能には適していません。

(3) アクション: データを格納するオブジェクト、つまりメッセージの伝達者は、他者によってのみ操作でき、単独で操作を実行することはできません。

熟考の結果、機能を追加できるのはアクションを送信するステップ、つまり、store.dispatch() メソッドのみです。たとえば、ロギング機能を追加し、Action と State を出力するには、store.dispatch を次のように変更します。

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

上記のコードでは、store.dispatch が再定義され、Action の送信前後に印刷機能が追加されています。これがミドルウェアの原型です。

ミドルウェアは、Actionの発行とReducerの実行という2つのステップの間に、store.dispatchメソッドを改変し、その他の機能を追加する機能です。一般的に使用されるミドルウェアは既製のものなので、他の人が作成したモジュールを参照してください。

2. ミドルウェアの使用方法

一般的に使用されるミドルウェアは既製のものであるため、このチュートリアルではミドルウェアの作成方法については説明しません。その他、それだけです。たとえば、前のセクションのログ ミドルウェアには、既製の redux-logger モジュールがあります。ここではミドルウェアの使い方のみを紹介します。

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

上記のコードでは、redux-logger はログ ミドルウェア ロガーを生成できるジェネレーター createLogger を提供します。そしてapplyMiddlewareメソッドに入れてcreateStoreメソッドに渡すことでstore.dispatch()の機能拡張が完了します。

注意点は 2 つあります:

(1) createStore メソッドはアプリケーション全体の初期状態をパラメータとして受け取ることができ、その場合、applyMiddleware が 3 番目のパラメータになります。

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

(2) ミドルウェアの順序は特別である必要があります。

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

上記のコードでは、applyMiddleware メソッドの 3 つのパラメーターは 3 つのミドルウェアです。一部のミドルウェアには順序要件があるため、使用する前にドキュメントを確認してください。たとえば、logger は最後に配置する必要があります。そうしないと、出力結果が正しくなくなります。

3. applyMiddlewares()

これを見ると、applyMiddlewares メソッドは正確に何をするのかと疑問に思うかもしれません。

これは Redux のネイティブ メソッドであり、その機能はすべてのミドルウェアの配列を形成し、順番に実行することです。以下はそのソースコードです。

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}
  }
}

上記のコードでは、すべてのミドルウェアが配列チェーンに配置され、実行のためにネストされ、最後にstore.dispatchが実行されます。ご覧のとおり、getState メソッドとdispatch メソッドはミドルウェア (middlewareAPI) 内で使用できます。

4. 非同期操作の基本的な考え方

ミドルウェアを理解すると、非同期操作を処理できるようになります。

同期操作では 1 つのアクションを発行するだけで済みますが、非同期操作との違いは、3 つのアクションを発行する必要があることです。

    #操作開始時のアクション
  • 操作成功時のアクション
  • 操作成功時のアクション操作は失敗します。 アクション
  • サーバーからのデータの取得を例として、3 つのアクションは 2 つの異なる方法で記述できます。
// 写法一:名称相同,参数不同
{ 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: { ... } }

さまざまなアクション タイプに加えて、さまざまな操作状態を反映するために非同期操作の状態も変更する必要があります。以下はStateの例です。

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

上記のコードでは、State のプロパティ isFetching は、データがフェッチされているかどうかを示します。 DidInvalidate はデータが古いかどうかを示し、lastUpdated は最終更新時刻を示します。

これで、非同期操作全体の概念が非常に明確になりました。

#操作が開始されたら、アクションを送信し、状態をトリガーして「動作中」状態に更新し、ビューを再レンダリングします
  • # #操作が完了したら、再度送信します。アクションが発行され、状態が「操作が終了しました」状態に更新されるようにトリガーされ、ビューが再度再レンダリングされます

  • # 5. redux-thunk ミドルウェア

  • 非同期操作では、少なくとも 2 つのアクションを送信する必要があります。ユーザーは最初のアクションをトリガーします。これは同期操作と同じですが、問題ありません。システムはどのようにすれば、操作終了時の 2 番目のアクション?

その秘密はアクションクリエイターにあります。
class AsyncApp extends Component {
  componentDidMount() {
    const { dispatch, selectedPost } = this.props
    dispatch(fetchPosts(selectedPost))
  }
// ...
上記のコードは、非同期コンポーネントの例です。正常にロードされた後 (componentDidMount メソッド)、サーバーからのデータ fetchPosts(selectedSubreddit) を要求するアクションを送信します (ディスパッチ メソッド)。ここでの fetchPosts は Action Creator です。

以下は fetchPosts のコードです。重要な点はそこにあります。

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是一个Action Creator(动作生成器),返回一个函数。这个函数执行后,先发出一个Action(requestPosts(postTitle)),然后进行异步操作。拿到结果后,先将结果转成 JSON 格式,然后再发出一个 Action( receivePosts(postTitle, json))。

上面代码中,有几个地方需要注意。

(1)fetchPosts返回了一个函数,而普通的 Action Creator 默认返回一个对象。

(2)返回的函数的参数是dispatch和getState这两个 Redux 方法,普通的 Action Creator 的参数是 Action 的内容。

(3)在返回的函数之中,先发出一个 Action(requestPosts(postTitle)),表示操作开始。

(4)异步操作结束之后,再发出一个 Action(receivePosts(postTitle, json)),表示操作结束。

这样的处理,就解决了自动发送第二个 Action 的问题。但是,又带来了一个新的问题,Action 是由store.dispatch方法发送的。而store.dispatch方法正常情况下,参数只能是对象,不能是函数。

这时,就要使用中间件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,使得后者可以接受函数作为参数。

因此,异步操作的第一种解决方案就是,写出一个返回函数的 Action Creator,然后使用redux-thunk中间件改造store.dispatch。

六、redux-promise 中间件

既然 Action Creator 可以返回函数,当然也可以返回其他值。另一种异步操作的解决方案,就是让 Action Creator 返回一个 Promise 对象。

这就需要使用redux-promise中间件。

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

这个中间件使得store.dispatch方法可以接受 Promise 对象作为参数。这时,Action Creator 有两种写法。写法一,返回值是一个 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属性是一个 Promise 对象。这需要从redux-actions模块引入createAction方法,并且写法也要变成下面这样。

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

上面代码中,第二个dispatch方法发出的是异步 Action,只有等到操作结束,这个 Action 才会实际发出。注意,createAction的第二个参数必须是一个 Promise 对象。

看一下redux-promise的源码,就会明白它内部是怎么操作的。

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 本身是一个 Promise,它 resolve 以后的值应该是一个 Action 对象,会被dispatch方法送出(action.then(dispatch)),但 reject 以后不会有任何动作;如果 Action 对象的payload属性是一个 Promise 对象,那么无论 resolve 和 reject,dispatch方法都会发出 Action。

以上が反応ミドルウェアとは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。