データストリームアーキテクチャ学習ノート(二)-Redux

9620 ワード

***私が作業中にプロジェクトに対してReduxを使用して状態管理とデータストリームを再構築する学習記録です.
Reduxの由来
2014年にFacebookはFluxアーキテクチャの概念と一方向データストリーム管理の思想を提出し、管理状態の基本データストリームを提供したが、フロントエンド応用の複雑性指数級の向上に伴い、フロントエンドページの管理が必要な状態もますます多くなり、Fluxの基本的なデータストリーム概念と一方向データストリーム思想に基づく多くの実現方式が現れた.2015年、Reduxが登場し、Fluxと関数式プログラミングを組み合わせ、短期間で最も人気のあるフロントエンドアーキテクチャとなった.
実際のプロジェクトでは、次のような状況に遭遇したことがあります.
  • debugプロジェクトで問題検索を行う場合、複雑なデータがページをリフレッシュする場合、いくつかの規範化されていない傍受と観察者のメカニズムがReactでthisを使用するか多すぎるためである.setStateはページをレンダリングします.非同期でデータをレンダリングするページなので、今回のデータ状態の変化によってページがレンダリングされたのか判断できないことがよくあります.さらに悪いことに、現在の状態で、あなたのAppの実際のデータ状態がどうなっているのかを知ることができません.つまり、あなたがAppの現在のすべてのデータがどれだけあるかを知ることができません.次のAppがどのように変化するかを迅速に予測することはできません.Reduxを使えばこの問題をうまく解決できます.
  • 同じように、プロジェクトでモジュール分割、コンポーネント化開発を行う場合は、Reduxを使用してモジュールコンポーネントをプロジェクトに迅速に組み込み、分割・再編成することができます.

  • Redux動作原理
    Reduxは自分を「予測可能な状態容器」と標榜し、関数式の特性を十分に利用し、全体をより優雅で純粋に実現し、使用も簡単にした.
    Redux(oldState) => newState
    

    ReduxはFluxの進化と見なすことができる.Reduxは、次の3つの基本原則に従います.
  • アプリケーション全体で唯一の信頼できるデータソース、つまり1つのStoreのみ
  • StateはActionをトリガすることでしか変更できない
  • Stateの変更は純粋な関数として書かなければならない.つまり、変更するたびに新しいStateが返される.ReduxではReducerと呼ぶ
  • View        —> Actions        Store —> Store    state —>    View。
    

    Reduxのアプリケーション全体のステータスはobject treeに格納され、一意のStoreに対応し、stateは読み取り専用であり、純粋な関数reducerを使用してstateを更新すると、元のものを直接変更するのではなく、新しいstateが生成されます.
    Reduxは以上の制約によりstateの変化を予測できる.
    これらの概念が理解できない場合は、Reduxの公式ドキュメントを勉強してから、他人のブログや使い方を見てから、より速く使用することをお勧めします.
    Reduxインスタンスパッケージ
    ここでReact Native実プロジェクト登録部では、ReduxをReact Native開発に適用してデータ管理を行う方法を示し、実アーキテクチャプロジェクトでは、それぞれの符号化習慣があるため、同じReduxであっても、各部分でコードの書き方が常に異なり、実プロジェクトで用いられる書き方も異なる.しかし、思想は全体的に同じで、コードの書き方にこだわらないで、コードはただ参考と総括として、書き方の目的思想とどのようにReduxの溶け込みと使用を体現するかを理解しなければならない.
    ビューレイヤView
    ログインページ:
    import { bindActionCreators } from 'redux';
    import { connect } from 'react-redux';
    import LoginAction from '../../actions/loginAction';
    ...
    class Login extends Component {
        ...
        _doLogin = () => {
            const param = {
              uid: this.state.uid,
              pwd: this.state.pwd
            };
        
            this.props.actions.doLogin(param)
              .then(() => {
                const { navigation, login } = this.props;
                if (login.status === 'done' && navigation) {
                  navigation.resetRouteTo('TabBar', { title: '  ', selectedTab: 'home' });
                } else {
                  Alert.alert(
                    '  ',
                    login.message
                  );
                }
              });
          };
        ...
    }
    ...
    const mapStateToProps = (state) => {
      return {
        login: state.loginReducer
      };
    };
    const mapDispatchToProps = dispatch => {
      return ({
        actions: bindActionCreators({ ...LoginAction }, dispatch)
      });
    };
    export default connect(mapStateToProps, mapDispatchToProps)(Login);
    

    簡単に言えば、View層の主な役割はユーザーの操作に応答することであり、実際にコードの中で私たちの主な役割はActionをトリガすることであり、例えばコードの中でthisを呼び出すことである.props.actions.doLogin()関数、ここにあります.propsにactions属性が存在するのは、最後にbindActionCreatorsメソッドを使用して対応するLoginActionをページコンポーネントLoginにバインドするためです.これにより、Viewレイヤではactionを呼び出す操作しかできず、dispatchを直接使用してメッセージを配布することはありません.これでView -> Actionsのプロセスが完了しました.
    動作アクション
    
    const _loginSuccess = (data) => {//eslint-disable-line
      return {
        type: ActionTypes.LOGIN_SUCCESS,
        payload: {
          user: data.uid
        }
      };
    };
    
    const _loginFailed = (error) => {
      return {
        type: ActionTypes.FAIL,
        payload: {
          message: error.message
        }
      };
    };
    
    const _doLogin = (url, param) => dispatch => {
      dispatch(CommonAction.showLoading());
      return Fetcher.postQsBodyFetch(url, param)
          .then((response) => {
            dispatch(CommonAction.dismissLoading());
            dispatch(_loginSuccess(param, response));
          }).catch((error) => {
            dispatch(CommonAction.dismissLoading());
            dispatch(_loginFailed(error));
          });
    };
    
    const LoginAction = {
      doLogin: (param) => _doLogin(NetLink.login, param),
      loginSuccess: (data) => _loginSuccess(data),
      loginFailed: (error) => _loginFailed(error),
    };
    
    

    Actionは、通常、bindActionCreatorsメソッドとコンポーネントバインディングがViewレイヤで使用されているため、Viewレイヤコンポーネントdispatchプロパティメソッドを直接取得し、Actionの純粋な関数でデータが返された後にdispatch()を呼び出してデータ配信を行う.これでActions -> Reducerのプロセスが完了しました.
    Reducer
    import ActionType from '../constants/actionType';
    
    const initialState = {
      status: 'init',
      user: '',
      message: null
    };
    
    const loginReducer = (state = initialState, action) => {
      switch (action.type) {
        case ActionType.LOGIN_SUCCESS:
          return Object.assign({}, state, {
            status: 'done',
            user: action.payload.user,
          });
        case ActionType.FAIL:
          return Object.assign({}, state, {
            status: 'fail',
            message: action.payload.message,
          });
        default:
          return state;
      }
    };
    
    

    Reducerは元のFluxのstoreのように、データウェアハウスのソースとしてdipatch()を呼び出して取得したメッセージを受け取り、処理と保存を行い、データをタイムリーに更新した後、reduxのコンポーネントバインドを通じて、ページコンポーネントに自動的にフィードバックしてデータ更新と非同期レンダリングを行います.ここではreturnの新しいオブジェクトをreturnする必要があります.reduxは、現在のコンポーネント関連のreducerを更新したことを知ることができます.このステップでは、データの状態がどのようにViewにフィードバックされ、あなたが書いた一般的なActionやReducerなどのjsファイルがどのようにコンポーネントとアプリケーションに関連しているかに疑問を抱くはずです.
    バインドと導入
    入口アセンブリRoot.js:
    import React, { Component } from 'react';
    import { Provider } from 'react-redux';
    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import Fetcher from './network/fetcher';
    import Main from './containers/mainContainer';
    import rootReducer from './reducers/rootReducer';
    
    const middlewares = [thunk];
    const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
    const createLogger = require('redux-logger');
    
    if (process.env.NODE_ENV === 'development') {
      const logger = createLogger();
      middlewares.push(logger);
    }
    
    function configureStore(initialState) {
      const store = createStoreWithMiddleware(rootReducer, initialState);
      return store;
    }
    
    const store = configureStore();
    
    export default class Root extends Component {
      constructor(props) {
        super(props);
        Fetcher.initNetworkState();
      }
    
      componentWillUnmount() {
        Fetcher.removeNetworkStateListener();
      }
    
      render() {
        return (
          
            
    ); } }

    其中rootReducer.js:

    import { combineReducers } from 'redux';
    import LoginReducer from './loginReducer';
    ...
    
    const rootReducer = combineReducers({
      loginReducer: LoginReducer,
      ...
    });
    
    export default rootReducer;
    

    まずcombineReducerを使用してすべてのreducerを1つのrootReducerにマージし、rootReducerを使用して、次にcreateStoreメソッドによるstoreオブジェクトの作成を開始し、reduxが提供するProviderコンポーネントによってstoreオブジェクトを実際のViewコンポーネントに直接バインドすることで完了します.
    View        —> Actions        Store —> Store    state —>    View。
    

    その中のapplyMiddlewarebindActionCreatorsなどの方法は、非同期actions、非同期データストリーム、Middlewareのステップアップに関する知識である.詳細については、Reduxの公式ドキュメントを参照してください.以下のReduxステップでは、彼らの使用となぜ使用するのかを大まかに説明します.
    Reduxステップアップ
    redux-thunkとredux-loggerフレームワークを使用し、applyMiddlewareとMiddlewareを使用してReduxの使用を強化し、Reduxをより強力で規範的で合理的にします.ここでredux-loggerフレームワークは、開発者がReduxのdispatchログをどのように印刷するかを知る必要はありませんが、開発時にdebugなどに使用するのは有用なツールです.redux-thunkは、非同期Actionsおよび非同期データストリームを処理するフレームワークに属する.
    非同期Actionsと非同期データストリーム
    非同期Actionsと非同期データストリームとは、簡単に言えば、ネットワーク要求によって制御されるActionsである.Appでボタンをクリックするとすぐにdispatch()が発行されます.これはApp制御に対して作成されたdispatch()です.これを同期Actionsと言いますが、非同期Actionsはあなたが制御したものではありません.ネットワーク要求が成功したり失敗したりした後、dispatch()が発行されます.これは非同期Actionsです.このdispatch()がいつ作成された操作なのか分かりません.あなたが成功したdispatch()や失敗したdispatch()を出していることもわかりません.
    私のloginActionの方法のように、この方法がこのように書かれている原理をよく理解できるはずです.
    const _doLogin = (url, param) => dispatch => {
      dispatch(CommonAction.showLoading());
      return Fetcher.postQsBodyFetch(url, param)
          .then((response) => {
            dispatch(CommonAction.dismissLoading());
            dispatch(_loginSuccess(param, response));
          }).catch((error) => {
            dispatch(CommonAction.dismissLoading());
            dispatch(_loginFailed(error));
          });
    };
    

    Middleware
    middlewareは中国語の意味のミドルウェアに翻訳されており、redux-thunkやredux-promiseのようにミドルウェアと呼ぶことができます.これらのミドルウェアを使用するには、applyMiddlewareなどの関連方法を使用してプロジェクトにこれらのフレームワークを追加する必要があります.プロジェクトのreduxの使用をより強力にし、規範化します.
    redux-thunkやredux-promiseのように非同期をサポートするmiddlewareにはstoreのdispatch()メソッドがパッケージされており、action以外の他のコンテンツ、例えば関数やPromiseをdispatchすることができます.あなたが使用している任意のmiddlewareは、dispatchの任意の内容を独自の方法で解析し、次のmiddlewareにactionsを渡し続けることができます.たとえば、Promiseをサポートするmiddlewareは、Promiseをブロックし、Promiseごとに非同期にbegin/end actionsのペアをdispatchすることができます.
    middlewareチェーンの最後のmiddlewareがdispatch actionを開始する場合、このactionは通常のオブジェクトでなければなりません.これは同期式のReduxデータストリームが始まる場所です(ここでは、任意の多非同期のmiddlewareを使用してやりたいことをすることができますが、通常のオブジェクトを最後のdispatchされたactionとして使用して、処理プロセスを同期方式に戻す必要があります).
    middlewareは非同期API呼び出しを含む様々なことを完了することができ,その進化過程を理解することはかなり重要なことである.彼らがどのように進化し、どのようにあなたの応用を強化したのか、ここでは具体的に説明しません.
    まとめ
    Reduxは強く、比較的複雑です.簡単なプロジェクトは必要ないかもしれませんが、プロジェクトがますます大きくなり、データが複雑になると、Reduxはプロジェクトをより規範的で丈夫にします.プロジェクトで規範的なフレームワークアーキテクチャを使用することは、非常に重要なことです.プロジェクトのためにアーキテクチャを使用することができます.これは面白いことです.
    文章はとても长くて、Reduxもとても复雑で、文の中でもし间违いがあれば教えてください、私は直ちに改正します.一緒に進歩して勉強します.ありがとう!
    リファレンス
    Redux中国語ドキュメント
    Redux入門チュートリアル-チェン一峰