応答テクノロジー(冗長ミドルウェアによる非同期タスクの管理)-1


Webアプリケーションに応答してAPIサーバをバインドする場合は、APIリクエストのステータスも管理する必要があります.
要求開始時:ロード中
要求が成功または失敗した場合:ロードが完了したことを示します.
成功した場合:管理サーバが受信した応答のステータス
失敗した場合:管理サーバが返すエラーのステータス
応答プロジェクトでreduceが使用され、これらの非同期操作を管理する必要がある場合は、「ミドルウェア」を使用して、非常に効率的で便利なステータス管理を行うことができます.
この章では、Ridexミドルウェアの概念を理解し、非同期タスクを処理する方法を学習します.
実習プロセス.
  • 作業環境準備
  • 直接作成
  • ミドルウェア
  • Redux-Logger
  • を使用
  • ミドルウェアを使用して非同期タスク
  • を管理

    18-1. 作業環境の準備

    yarn create react-app learn-redux-middleware
    cd learn-redux-middleware
    yarn add redux react-redux redux-actions
    Ridex用のコードの準備
    まず、反放射モジュールを作成します.
    modules/counter.js
    import { createAction, handleActions } from "redux-actions";
    
    const INCREASE = "counter/INCREASE";
    const DECREASE = "counter/DECREASE";
    
    export const increase = createAction(INCREASE);
    export const decrease = createAction(DECREASE);
    
    const initialState = 0; // 상태는 꼭 객체일 필요가 없다. 숫자도 작동
    
    const counter = handleActions(
      {
        [INCREASE]: (state) => state + 1,
        [DECREASE]: (state) => state - 1,
      },
      initialState
    );
    
    export default counter;
    スーパーユーザーの作成
    modules/index.js
    import { combineReducers } from "redux";
    import counter from "./counter";
    
    const rootReducer = combineReducers({ counter });
    
    export default rootReducer;
    srcディレクトリのインデックス.jsにショップを作成し、プロバイダアプリケーションに応答プロジェクトにコピーします.
    import React from "react";
    import ReactDOM from "react-dom";
    import { createStore } from "redux";
    import { Provider } from "react-redux";
    import "./index.css";
    import App from "./App";
    import rootReducer from "./modules";
    
    const store = createStore(rootReducer);
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById("root")
    );
    カウンタ構成部品とカウンタコンテナ構成部品の作成
    components/Counter.js(Presentation素子)
    import React from "react";
    
    const Counter = ({ onIncrease, onDecrease, number }) => {
      return (
        <div>
          <h1>{number}</h1>
          <button onClick={onIncrease}>+1</button>
          <button onClick={onDecrease}>-1</button>
        </div>
      );
    };
    
    export default Counter;
    containers/CounterContainer.js(容量素子)
    import React from "react";
    import { connect } from "react-redux";
    import { increase, decrease } from "../modules/counter";
    import Counter from "../components/Counter";
    
    const CounterContainer = ({ number, increase, decrease }) => {
      return (
        <Counter number={number} onIncrease={increase} onDecrease={decrease} />
      );
    };
    
    export default connect(
      (state) => ({
        number: state.counter,
      }),
      {
        increase,
        decrease,
      }
    )(CounterContainer);
    ComputerContainerをAppに表示する
    import React from "react";
    import CounterContainer from "./containers/CounterContainer";
    
    const App = () => {
      return (
        <div>
          <CounterContainer />
        </div>
      );
    };
    
    export default App;

    18-2. ミドルウェアとは?


    ミドルウェア:動作(トリガ動作)を派遣する場合、Reducerがその動作を処理する前に予め指定されたタスクを実行します.(ミドルウェアはアクションとヘビー級ボクサーの中間者)

    ミドルウェアコース?

  • 動作→ミドルウェア→リカバリプログラム→ショップ
  • ミドルウェアは、Reduserが動作を処理する前に実行できる動作
  • が送信動作は、コンソールにのみ
  • が書き込まれる.
  • で受信動作情報に基づいて、動作
  • をキャンセルする.
  • 他のタイプの動作プログラム
  • を追加する.

    18-2-1. ミドルウェアの作成


    実際のプロジェクト作業では,直接中路ソフトウェアを作成して使用する作業は多くない.他の開発者が作成したミドルウェアを使えばいいのですが、これは初めてなので理解が必要なので、自分で作成してどのように働いているのか理解してみましょう.
    レコードミドルウェアを作成し、デバッグ動作のたびにコンソールに動作の情報と動作デバッグ前後の状態を表示します.
    src/lib/loggerMiddleware.js
    const loggerMiddleware = (store) => (next) => (action) => {
      // 미들웨어 기본 구조
    };
    
    export default loggerMiddleware;
    上のコードはRidexミドルウェア構造と呼ばれています.矢印関数をfunctionキーとして展開して書き込む
    const loggerMiddleware = function loggerMiddleware (store) {
    	return function (next) {
    		return function (action) {
    			// 미들웨어 기본 구조
    		}
    	}
    }
    ミドルウェアは最終的に戻り関数の関数と呼ばれます.ここで,関数でパラメータとして受信するstoreはリカバリインスタンス,actionは派遣された動作を指す.
    その2つとは異なり、nextはよく知られておらず、nextパラメータは関数形式、storeである.ディスパッチと似たような役割を果たしているそうです.違いは、next(action)が呼び出されると、次の処理が必要なミドルウェアに動作が伝達され、次のミドルウェアがなければReduserに動作が伝達されることである.

    ミドルウェア内部store.dispatchを使用して、最初のミドルウェアから再処理を開始します.ミドルウェアにnextが使用されていない場合、アクションは再生プログラムに渡されません.(無視アクション)
    今回のミドルウェアは、コンソールに情報を順次表示します.
  • 移行ステータス
  • 動作情報
  • 新状態
  • const loggerMiddleware = (store) => (next) => (action) => {
      console.log(action && action.type); // 액션 타입으로 log를 그룹화
      console.log("이전 상태", store.getState());
      console.log("액션", action);
      next(action); // 다음 미들웨어 혹은 리듀서에게 전달
      console.log("다음 상태", store.getState()); // 업데이트된 상태
      console.groupEnd(); // 그룹 끝
    };
    
    export default loggerMiddleware;
    作成した冗長ミドルウェアをショップに適用します.ミドルウェアはショップの作成に適用されます
    index.js
    import React from "react";
    import ReactDOM from "react-dom";
    import { createStore, applyMiddleware } from "redux";
    import { Provider } from "react-redux";
    import "./index.css";
    import App from "./App";
    import rootReducer from "./modules";
    import loggerMiddleware from "./lib/loggerMiddleware";
    
    const store = createStore(rootReducer, applyMiddleware(loggerMiddleware));
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById("root")
    );

    18-2-2. redux-loggerの使用


    オープンソースコミュニティのredux-loggerミドルウェアをインストールして使用します.先ほど作成したloggerMiddlewareよりも良いライブラリで、ブラウザコンソールのフォーマットも簡潔だそうです.
    yarn add redux-logger
    index.js
    import React from "react";
    import ReactDOM from "react-dom";
    import { createStore, applyMiddleware } from "redux";
    import { Provider } from "react-redux";
    import "./index.css";
    import App from "./App";
    import rootReducer from "./modules";
    // import loggerMiddleware from "./lib/loggerMiddleware";
    import { createLogger } from "redux-logger";
    
    const logger = createLogger();
    const store = createStore(rootReducer, applyMiddleware(logger));
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById("root")
    );

    18-3. ミドルウェアを使用した非同期タスクの処理


    非同期タスクを処理するときに役立つミドルウェアは本当にたくさんあります.本書で紹介するミドルウェア

    18-3-1. redux-thunk


    Ridexを使用するプロジェクトで非同期操作を処理する際に最も基本的に使用されるミドルウェア
    オブジェクトではなく関数形式の動作でdispatchを許可

    18-3-1-1. Thunkとは?


    Thunkは,特定のタスクを関数として後回しにすることを意味する.

    18-3-1-2. アプリケーションミドルウェア

    yarn add redux-thunk
    ショップの作成時にredux-thunkを適用
    index.js
    import React from "react";
    import ReactDOM from "react-dom";
    import { createStore, applyMiddleware } from "redux";
    import { Provider } from "react-redux";
    import "./index.css";
    import App from "./App";
    import rootReducer from "./modules";
    // import loggerMiddleware from "./lib/loggerMiddleware";
    import { createLogger } from "redux-logger";
    import ReduxThunk from "redux-thunk";
    
    const logger = createLogger();
    const store = createStore(rootReducer, applyMiddleware(logger, ReduxThunk));
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById("root")
    );

    18-3-1-3. Thunk生成関数の作成


    redux-thunkは、通常のアクションオブジェクトchellではなく、アクション生成関数で関数を返します.
    increationsyncとdecreationsync関数を作成し、大きな数値を非同期で変更します.
    import { createAction, handleActions } from "redux-actions";
    
    const INCREASE = "counter/INCREASE";
    const DECREASE = "counter/DECREASE";
    
    export const increase = createAction(INCREASE);
    export const decrease = createAction(DECREASE);
    
    // 1초 뒤에 increase 혹은 decrease 함수를 디스패치함
    export const increaseAsync = () => (dispatch) => {
      setTimeout(() => {
        dispatch(increase());
      }, 1000);
    };
    export const decreaseAsync = () => (dispatch) => {
      setTimeout(() => {
        dispatch(decrease());
      }, 1000);
    };
    
    const initialState = 0; // 상태는 꼭 객체일 필요가 없다. 숫자도 작동
    
    const counter = handleActions(
      {
        [INCREASE]: (state) => state + 1,
        [DECREASE]: (state) => state - 1,
      },
      initialState
    );
    
    export default counter;
    Reduceモジュールを変更すると、ComputerContainerによって呼び出されたアクション作成関数が変更されます.
    import React from "react";
    import { connect } from "react-redux";
    import { increaseAsync, decreaseAsync } from "../modules/counter";
    import Counter from "../components/Counter";
    
    const CounterContainer = ({ number, increaseAsync, decreaseAsync }) => {
      return (
        <Counter
          number={number}
          onIncrease={increaseAsync}
          onDecrease={decreaseAsync}
        />
      );
    };
    
    export default connect(
      (state) => ({
        number: state.counter,
      }),
      {
        increaseAsync,
        decreaseAsync,
      }
    )(CounterContainer);

    18-3-1-4. Webリクエストの非同期処理


    thunkのプロパティを使用して、Webリクエストの非同期操作を処理します.
    JSOnPlaceholder(https://jsonplaceholder.typicode.com)が提供する偽APIを使用してWebリクエストを練習します.使用するAPIは次のとおりです.
  • 記事の読み取り(:idは1~100の数字)

  • GET https://jsonplaceholder.typicode.com/posts/:id
  • すべてのユーザー情報を読み込み

  • GET https://jsonplaceholder.typicode.com/users
  • APIを呼び出す場合、PromiseベースのWebクライアントaxiosが一般的に使用されます.
    yarn add axios
    APIをすべて関数化します.個々のAPIを呼び出す関数を個別に記述すると,後で使用する際にその毒性とメンテナンスが容易になる.exportを使用してエクスポートし、他のファイルからロードおよび使用します.
    lib/api.js
    import axios from "axios";
    
    export const getPost = (id) =>
      axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
    export const getUsers = (id) =>
      axios.get(`https://jsonplaceholder.typicode.com/users`);
    上記のAPIを使用して、「サンプル」と呼ばれるレプリケーションを作成し、データを受信してステータスを管理します.
    modules/sample.js
    import { handleActions } from "redux-actions";
    import * as api from "../lib/api";
    
    // 액션 타입을 선언
    // 한 요청당 세 개를 만들어야 함
    
    const GET_POST = "sample/GET_POST";
    const GET_POST_SUCCESS = "sample/GET_POST_SUCCESS";
    const GET_POST_FAILURE = "sample/GET_POST_FAILURE";
    
    const GET_USERS = "sample/GET_USERS";
    const GET_USERS_SUCCESS = "sample/GET_USERS_SUCCESS";
    const GET_USERS_FAILURE = "sample/GET_USERS_FAILURE";
    
    // thunk 함수를 생성
    // thunk 함수 내부에서는 시작할 때, 성공했을 때, 실패했을 때 다른 액션을 디스패치한다.
    
    export const getPost = (id) => async (dispatch) => {
      dispatch({ type: GET_POST }); // 요청을 시작한 것을 알림
      try {
        const response = await api.getPost(id);
        dispatch({
          type: GET_POST_SUCCESS,
          payload: response.data,
        }); // 요청 성공
      } catch (e) {
        dispatch({
          type: GET_POST_FAILURE,
          payload: e,
          error: true,
        }); // 요청 실패
        throw e; // 나중에 컴포넌트단에서 에러를 조회할 수 있도록 해줌
      }
    };
    
    export const getUsers = () => async (dispatch) => {
      dispatch({ type: GET_USERS }); // 요청을 시작한 것을 알림
      try {
        const response = await api.getUsers();
        dispatch({
          type: GET_USERS_SUCCESS,
          payload: response.data,
        }); // 요청 성공
      } catch (e) {
        dispatch({
          type: GET_USERS_FAILURE,
          payload: e,
          error: true,
        }); // 에러발생
        throw e; // 나중에 컴포넌트단에서 에러를 조회할 수 있게 해 줌
      }
    };
    
    // 초기 상태를 선언
    // 요청의 로딩 중 상태는 loading이라는 객체에서 관리
    
    const initialState = {
      loading: {
        GET_POST: false,
        GET_USERS: false,
      },
      post: null,
      users: null,
    };
    
    const sample = handleActions(
      {
        [GET_POST]: (state) => ({
          ...state,
          loading: {
            ...state.loading,
            GET_POST: true, // 요청시작
          },
        }),
        [GET_POST_SUCCESS]: (state, action) => ({
          ...state,
          loading: {
            ...state.loading,
            GET_POST: false, // 요청 완료
          },
          post: action.payload,
        }),
        [GET_POST_FAILURE]: (state, action) => ({
          ...state,
          loading: {
            ...state.loading,
            GET_POST: false, // 요청 완료
          },
        }),
        [GET_USERS]: (state) => ({
          ...state,
          loading: {
            ...state.loading,
            GET_USERS: true, // 요청 시작
          },
        }),
        [GET_USERS_SUCCESS]: (state, action) => ({
          ...state,
          loading: {
            ...state.loading,
            GET_USERS: false, // 요청 완료
          },
          users: action.payload,
        }),
        [GET_USERS_FAILURE]: (state, action) => ({
          ...state,
          loading: {
            ...state.loading,
            GET_USERS: false, // 요청 완료
          },
        }),
      },
      initialState
    );
    
    export default sample;
    コードに繰り返される論理はかなり多い.まず、コンテナ構成部品を使用してデータ要求を正常に処理し、その後、繰り返しの論理を分離して、コードを再使用する形で再パッケージします.
    翻訳機は既に完成しており、この翻訳機を翻訳機に含める.
    modules/index.js
    import { combineReducers } from "redux";
    import counter from "./counter";
    import sample from "./sample";
    
    const rootReducer = combineReducers({ counter, sample });
    
    export default rootReducer;
    今回作成するコンポーネントではpostはtitleとbodyのみ、userはユーザー名とemailのみを表示します.
    components/Sample.js
    import React from "react";
    
    const Sample = ({ loadingPost, loadingUsers, post, users }) => {
      return (
        <div>
          <section>
            <h1>포스트</h1>
            {loadingPost && "로딩중..."}
            {!loadingPost && post && (
              <div>
                <h3>{post.title}</h3>
                <h3>{post.body}</h3>
              </div>
            )}
          </section>
          <hr />
          <section>
            <h1>사용자 목록</h1>
            {loadingUsers && "로딩 중..."}
            {!loadingUsers && users && (
              <ul>
                {users.map((user) => (
                  <li key={user.id}>
                    {user.username} ({user.email})
                  </li>
                ))}
              </ul>
            )}
          </section>
        </div>
      );
    };
    
    export default Sample;
    データのロードとレンダリングには、有効性チェックが重要です.
    たとえば、post&&&はpostオブジェクトが有効な場合にのみ内部postを使用します.見出しbody値が表示されます.データがなければpost.titleをクエリーしようとするとjavascriptエラーが発生し、検証する必要があります.
    ユーザも同様にデータが配列形式で入ることを期待し,map関数を用いる.
    ただし、有効性チェックを行わないとnull値に対してmap関数が呼び出され、最終的にmap関数が存在しないためエラーが発生します.