フロントエンドの状態管理(パート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): テーブル ヘッダー

推薦する

CentOS 7 で MySQL 8 の複数のインスタンスを設定する詳細なチュートリアル (必要な数だけ設定できます)

原因最近、プロジェクトのリファクタリングを始めたのですが、マスタースレーブと読み取り書き込み分離を使...

MySQLを監視するためのbinlogログ解析ツールの詳しい説明:Canal

Canal は、Java を使用して開発された Alibaba のオープンソース プロジェクトです...

MySQL は重複データを削除して最小の ID ソリューションを維持します

オンラインで検索して重複データを削除し、ID が最小のデータだけを残します。方法は次のとおりです。 ...

Vue のディスパッチとブロードキャストの自己実装の詳細説明 (ディスパッチとブロードキャスト)

解決すべき問題主にコンポーネント間のクロスレベル通信用なぜディスパッチとブロードキャストを自分で実装...

MySql インデックスの詳細な紹介と正しい使用方法

MySql インデックスの詳細な紹介と正しい使用方法1. はじめに:インデックスはクエリ速度に重大な...

uni-appのスタイルの詳細な説明

目次uni-app のスタイル要約するuni-app のスタイルsassプラグインは公式ウェブサイト...

HTTP サーバーとクライアントのやり取りをシミュレートする Node.js+postman

目次1. NodeがHTTPサーバーを構築する2. HTTPサーバーがリクエストを取得する1. Po...

Windows Server 2008R2 ファイル サーバーを Windows Server 2016 にアップグレードする

ユーザー組織には、ドメインに参加している 2 台の Windows Server 2008 R2 フ...

要素の属性を削除する JS removeAttribute() メソッド

JavaScript では、要素の removeAttribute() メソッドを使用して、指定され...

jQuery を使用してカルーセル効果を実装する

この記事では、jQueryでカルーセルチャートを実装するための具体的なコードを参考までに共有します。...

VMware vSphere 6.7 (ESXI 6.7) のグラフィック インストール手順

環境: VMware VCSA 6.7 (VMware-VCSA-all-6.7.0-8169922...

単一の Nginx IP アドレスに複数の SSL 証明書を設定する例

デフォルトでは、Nginx は IP アドレスごとに 1 つの SSL 証明書のみをサポートします。...

HTML で dl(dt,dd)、ul(li)、ol(li) を使用する方法

HTML <dl> タグ#定義と使用法<dl> タグは定義リストを定義します...

HTMLでマスクレイヤーを実装する方法 HTMLでマスクレイヤーを使用する方法

Web ページでマスク レイヤーを使用すると、繰り返しの操作を防ぎ、読み込みを促進できます。また、ポ...