フロントエンドの状態管理(パート2)

フロントエンドの状態管理(パート2)

序文:

前回の記事「フロントエンドの状態管理(パート 1)」に引き続き、これほど多くの読者が注目するとは思っていませんでした。皆さんのサポートと提案に感謝します。ここでは、個人的な意見や考えを述べているだけなので、十分に網羅的ではないかもしれません。Vue の使用例は単なるガイドであり、Vue にも高い注目度があります。友人の中には、Vuex 以外のソリューションを聞きたいという人もいます。今日は、Redux から始めて、徐々に拡張していきます (タイトルの通り)。

1. 再出発

Reactファミリーの一員として、 Redux Reactアプリケーションに予測可能な状態管理メカニズムを提供しようとします。ほとんどの状態管理ソリューションと同様に、Redux の考え方もパブリッシュ/サブスクライブ モデルです。Redux を簡単に理解するために、ライブラリを例に挙げてみましょう。

Redux の基本的な操作は、おおよそ次のようになります。

  • Store (司書)
  • State (書籍)
  • Action (図書貸出リスト)
  • store.dispatch (借用リストの送信)
  • Reducer (書籍の梱包)
  • store.subscribe (書籍を受け取る)

1.1. ストア(司書)

Storeコンテナとみなすことができ、アプリケーション全体でStore 1 つだけ存在します。本を借りたいときは図書館員のところに行くしかないようなものです。

'redux' から {createStore} をインポートします。
const store = createStore(リデューサー);

1.2. 状態(書籍)

Stateについては、 Actionを通じてのみ変更できます (つまり、借用フォームを送信することによってのみ本を借りることができます)。また、 Stateの値は直接変更しないでください。

stateを取得するには、 store.getState()を使用します。

'redux' から {createStore} をインポートします。
const ストア = createStore(リデューサー)
ストア.getState()

1.3. アクション(図書貸出リスト)

本を借りたい場合はどうすればいいですか?もちろん、図書貸出リストを管理者に提出する必要があります。すると、ユーザーはStateにアクセスできなくなり、 View を通じてのみ操作(ボタンをクリックするなど)できるようになります。つまり、 Stateの変更はViewの変更に対応するため、 View はState変更を通知するためにActionを送信する必要があります。 (貸出リストを管理者に提出すると、一連の操作が行われます)

Actionはカスタム オブジェクトであり、そのタイプ属性は実行される合意された操作です。

定数アクション = {
    タイプ: 'クリック'、
    情報: '借用リストを送信'
}

1.4、store.dispatch (貸出リストの送信)

store.dispatch 、View がアクションを送信する唯一の方法です。 Actionオブジェクト (つまり、貸出リストの送信) を受け入れ、リスト情報を司書に渡すだけで、司書はリストに基づいて対応する書籍を検索します。

store.dispatch(アクション)

1.5. リデューサー(書籍のパッケージング)

Store Action受け取った後、 View が変更されるように新しい State を与える必要があり、新しいStateの計算プロセスはReducerによって完了します。 (注文品を受け取ったら、本を袋などに詰めてください。)

Reducer 、Action と現在のStateパラメーターとして受け入れ、新しい State を返すカスタム関数です。

const リデューサー = (状態、アクション) => {
    return `action.info: ${state}` // => 借用リストを送信: 紅楼夢}

store.subscribe (書籍を受け取る)
状態が変化すると、store.subscribe() がそれをリッスンし、View を自動的に更新します。

const 購読解除 = store.subscribe(() => {
  与える() {
    // ビューを更新する 
  }
})
// 購読を解除することもできます(聞く)
登録解除()

まとめ:

Reduxに触れたばかりの学生はReduxかなり複雑に感じると思いますが、これはRedux内のすべてが確実であるべきという彼の考えにも関係しています。

Reduxですべてを決定論的にすることはまだ不可能ですが (非同期など) 、次の部分を含め、ほとんどの部分は決定論的であることが保証されるはずです。

  • ビューのレンダリングは決定論的である
  • 国家の再建は決定論的である

なぜこれをやるべきなのかについては、前回の記事ですでに述べました。その重要性は、アプリケーションのテスト、エラー診断、 Bug修正を容易にすることにあります。

2. 国家管理の目的

実際、ほとんどのプログラマーが Redux を使用する最も一般的なシナリオは、ページ A からページ B に戻り、ページ B のステータスを保存する必要があることです。

プロジェクトが大きくない場合、 Reduxまたは Vuex を使用すると少し大きく見えるでしょうか? Vue現在のコンポーネントをキャッシュできるようにするkeep-aliveを提供しており、これにより上記のシナリオを解決できることがわかっています。

しかし残念ながら、 Reactには Vue のようなkeep-aliveがありません。コミュニティでの一般的な解決策はルーティングを変更することですが、この変更はプロジェクトにあまりにも侵襲的であり、保守が困難です。さらに、 react-router v5ではルーティングフックもキャンセルされています。したがって、小規模なプロジェクトの場合は、関数を自分でカプセル化することをお勧めします。 (もちろん、必要に応じてReduxを使用することもできますが、私たちはより多くの方法を模索しているところです)

図書館を例に考えてみましょう。現在、図書館管理システムがあります。一覧ページ(list)からdetailページ(detail)にジャンプした際に、一覧ページの状態(検索バーの状態など)を保存する必要があります。

使用するテクノロジー スタックが ( react + antd ) であると仮定して、単純で大まかなものを手動で記述します (コアは、 contextを使用してコンポーネント間でデータを転送することです)。

//KeepAlive.js
デフォルト関数keepAliveWrapper()をエクスポートする{
  関数keepAlive(WrappedComponent)を返す{
    戻りクラスKeepAliveはWrappedComponentを拡張します{ // ps
      コンストラクタ(props) {
        スーパー(小道具)
        // 何かを実行する...
      }

      コンポーネントマウント() {
        定数{
          キープアライブ: { フィールド値 },
        } = this.context
        // 何かを実行する...
        super.componentDidMount()

      }

      与える() {
        // 何かを実行する...
        super.render() を返す
      }
    }
  }
}

元のコンポーネントを継承する必要がある理由は次のとおりです(// ps)

従来の記述方法でクラスコンポーネント( class KeepAlive extends React.Component )を返す場合、それは本質的にネストされた親子コンポーネントです。親コンポーネントと子コンポーネントのライフサイクルは順番に実行されるため、状態を取得するためにリストページに戻るたびに2回レンダリングされます。これは、 HOCによって返された親コンポーネントが元のコンポーネントのメソッドを呼び出すため、リストページが2回要求され、2回レンダリングされるためです。

HOC (高次コンポーネント)が元のコンポーネントから継承すると、2つのライフサイクルが交互に実行されなくなり、この問題はうまく解決されます。

// main.jsx ルートコンポーネント import React from 'react'

const appContext = React.createContext()

クラスAppはReact.Componentを拡張します。
    コンストラクタ(props) {
        スーパー(小道具)
        この状態 = {
          keepAlive: {}, // キャッシュオブジェクト isCache: false, // キャッシュするかどうか fieldsValue: {} // フォームの値をキャッシュする }
    }
    コンポーネントマウント() {
        // 初期化 const keepAlive = {
          isCache: this.state.isCache、
          トグル: this.toggleCache.bind(this),
          フィールド値: this.state.fieldsValue、
        }
        this.setState({keepAlive}) を実行します。
    }
    // レンダリング警告を防ぐために状態をクリアするメソッドです (レンダリング前にフィールドを設定することはできません...)
    // たとえば、list1 => list1/detail => list2 では、次のコールバックにジャンプを配置し、ステータスをクリアする必要があります。toggleCache(isCache = false, payload, callback) {
        const { fieldsValue = null } = ペイロード
        定数キープアライブ = {
          キャッシュ、
          フィールド値、
          トグル: this.toggleCache.bind(this),
        }
        const fn = typeof callback === 'function' ? callback() : void 0
        this.setState() は、
          {
            キープアライブ、
          },
          () => {
            注釈
          }
        )
    }
    与える() {
        const { キープアライブ } = this.state
        <appContext.Provider 値 = {{ keepAlive }}>
            // あなたのルート...
        </appContext.Provider>
    }

}

contextを直接使用せず、 keepAliveという追加レイヤーをカプセル化する理由は、 context処理を​​統一するためです。コンポーネント ヘッダーに decorator (@keepAlive)という簡潔な記述方法を使用すると、これがキャッシュされたコンポーネントであることがすぐにわかります (読みやすく、メンテナンスしやすいため)。

// ページで使用する場合は 'react' から React をインポートします
'../keepAlive' から keepAlive をインポートします

// keepAlive は元のコンポーネントに最も近い場所に配置する必要があります @keepAlive()
クラスAppはReact.Componentを拡張します。
    コンストラクタ(props){
        スーパー(小道具)
        この状態 = {
            // 何かを初期化します...
        }
    }
    コンポーネントマウント() {
        // 何かをする...
        if(this.context.keepAlive.fieldsValue) {
            const { tableList } = this.context.keepAlive.fieldsValue
            console.log('Cached:',tableList) // キャッシュ: ['1', '2']
        }
    }
    // 詳細を表示 detail = () => {
        this.context.keepAlive.fieldsValue = {
            テーブルリスト: ['1', '2']
        }
        // ジャンプ...
    }
    // list1 => list1/detail (次のメソッドは詳細ページにあるはずです) => list2 のように、第 1 レベルのルートをジャンプする必要がある場合は、警告 toList2 = () => { を処理する必要があります。
        this.context.keepAlive.toggle(false, {}, () => {
            // ジャンプ...
        })
    }
}

上記ではデコレータ記述を使用していますが、簡単に言うと、以下のbabelを設定するだけで使えますよ~

npm install -D @babel/plugin-proposal-decorators

jsconfig.jsonで設定します (存在しない場合は新規作成します)。

{
    "コンパイラオプション": {
        "実験的デコレータ": true
    },
    「除外」: [
        「ノードモジュール」、
        「距離」
    ]
}

.babelrc で設定します:

{
    「プラグイン」: [
        "@babel/プラグイン提案デコレータ",
        {
            「レガシー」: 真
        }
    ]
}

上記の方法は、先ほど述べたシナリオに適しています (ページ A からページ B に戻るには、ページ B の状態を保存する必要があります)。Redux またはMobx Reduxするほうがよいと言う人もいます。ルートをジャンプする場合は、警告を防ぐためにステータスを手動でクリアする必要があります。 。 。人によって意見は異なります。自分でカプセル化しているということは、それについてある程度研究しているという証拠ですね。簡単か難しいかは関係なく、プログラミングそのものは継続的な探求ではないでしょうか?ハハ。たとえ自分の書いたものが十分良くなかったとしても、批判は謙虚に受け止めてください。結局のところ、世の中には才能のある人がたくさんいます。

要約:

フロントエンドステータス管理に関するこの記事はこれで終わりです。フロントエンドステータス管理に関するその他のコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

前回の記事のレビュー: フロントエンドの状態管理についての簡単な説明 (パート 1)

以下もご興味があるかもしれません:
  • フロントエンドの状態管理(パート 1)
  • Vue フロントエンド開発補助機能状態管理詳細例

<<:  Nginx tp3.2.3 404 問題の解決

>>:  HTML テーブル マークアップ チュートリアル (18): テーブル ヘッダー

推薦する

MySQL ロックブロッキングの詳細な分析

日常のメンテナンスでは、スレッドがブロックされることが多く、データベースの応答が非常に遅くなります。...

MySQL シリーズ データベース設計 3 つのパラダイム チュートリアルの例

目次1. データベース設計の3つのパラダイムに関する知識の説明1. デザインパラダイムとは何ですか?...

最小限の展開で CentOS8 に OpenStack Ussuri をインストールする方法の詳細なチュートリアル

CentOS8 に最小限のデプロイメントで OpenStack Ussuri をインストールするため...

Vueインスタンスで$refsを使用する際の注意点

開発の過程では、インスタンスの vm.$refs(this.$refs) を使用して、ref で登録...

mysqlは内部コマンドエラーの解決策ではありません

「mysqlは内部コマンドではありません」というエラーは、mysqlのbinディレクトリパスが環境変...

Vueデータプロキシの詳細な説明

目次1. これからお話しするのは、フロントエンド担当者がvue-cliで完了できるソリューション、デ...

MySQLのどのフィールドがインデックスに適しているかについての簡単な説明

目次1 データベース インデックスを作成するための一般的なルールは次のとおりです。 2. 数千万件の...

ディスク容量不足による MySQL レプリケーション障害の解決方法

目次ケースシナリオ問題を解決するまとめケースシナリオ本日、オンラインで問題が発見されました。監視範囲...

Linux ファイル記述子、ファイルポインタ、および inode の詳細

目次Linux - ファイル記述子、ファイルポインタ、インデックスノード1. Linux - ファイ...

vuex の補助関数 mapGetters の基本的な使い方の詳細な説明

mapGettersヘルパー関数mapGettersヘルパー関数は、ストア内のゲッターをローカルの計...

Nginx+tomcat ロードバランシングクラスタの実装方法

実験環境は以下のとおりですここでは、4 台のサーバー (1 台の nginx、負荷用の 2 台の t...

...

Vue での this.$set の動的データバインディングのケーススタディ

インターネット上の this.$set の説明はわかりにくいと感じます。単一データ、オブジェクト、配...

レスポンシブ原則と Vue2.0/3.0 の違いについての簡単な分析

序文vue3.0 が正式にリリースされて以来、多くの友人が vue3.0 に切り替えました。ここでは...

Linux rpm および yum コマンドとその使用法の詳細な説明

RPM パッケージ管理インターネット ダウンロード パッケージのパッケージ化およびインストール ツー...