反応における高度な状態管理


理解状態
反応の主な仕事は、アプリケーションの状態を取り、それをDOMノードに変えることです.それはちょうどビュー層です.
反応状態のキーは乾いています:あなた自身を繰り返さないでください.あなたのアプリケーションが必要な状態の絶対最小表現を理解し、高需要に必要なすべてを計算します.
たとえば、FullNameの場合は、最初の名前と最後の名前の状態を持っていると考え、両方の変更を行うたびに、FullNameを移動して更新する必要はありません.ただ、既存の状態からfullnameを計算(取得)してみてください.
ここで、最低限の状態は最初の名前と最後の名前です、そして、fullnameは実際に州でありません、代わりにそれが裸の最小の状態から計算されるでしょう.
どのような状態ではないですか?
  • それが小道具を通して親から渡されるならば?もしそうならば、それは多分状態でありません.
  • それが時間とともに変わらないならば?もしそうならば、それは多分状態でありません.
  • あなたのコンポーネント内の任意の他の状態や小道具に基づいてそれを計算することはできますか?もしそうならば、それは状態でありません.
  • 一方向データフロー
    反応は、すべてのコンポーネント階層のデータフローについてです.どのコンポーネントがどの状態を所有するべきかはすぐには明らかでないかもしれません.
    国家対国家
    支柱は必ずしも状態ではありません、しかし、彼らは通常、他の誰かの状態です.これらは、変数やプロパティだけでなく、両方の状態にすることができます.
    状態は任意のコンポーネントで作成され、コンポーネントに残ります.それは子供の小道具として渡されることができます.
    州の種類
    アプリケーションのさまざまな地域で作成された状態は等しくありません.様々な種類がある.
    モデルデータ状態:コンポーネントを構成するための基本情報として機能するサーバーまたは外部ソースから副作用から取得される状態.
    ビュー/UI状態:ビューを更新する責任がある状態.たとえば、モーダルダイアログのオープン状態または閉じた状態を処理する状態です.
    3 .セッション状態:セッション状態の非常に良い例は、ユーザがログインしているかどうかを扱うauth状態です.
    (4)通信:ローダ、エラー、またはどのような段階で外部のデータからデータを取り出すかを示す成功状態(副作用).
    5 .場所:この状態は、実際にアプリケーションにどこにいるかを示します.私たちは実際にこのような状態を得るためにUSELocationのようなあらかじめ定義されたフックを使用します.しかし、現在の位置を維持するためのユーザ定義状態があるかどうかを調べます.
    時間に対する状態
    それは常に時間に相対的な状態について考える意味があります.
    1 .長期にわたる状態:これはアプリケーションのデータです.
    2 . Ephemeral state :入力したフィールドの値のように、Enterキーを押すとワイプされます.
    これらの質問
  • 入力フィールドは、モデルデータと同じ種類の状態管理を必要としますか?
  • フォームの検証と、どこにその状態があるのか?
  • それはあなたのすべてのデータを1つの場所に置くか、それを集中させることを意味しますか?
  • 両方に利点と欠点があります.
    基本反応成分
    最も単純な反応成分から始めましょう.次のコードには、カウンタの問題を実装する機能コンポーネントが含まれます.インクリメント、デクリメント、リセットの3つのボタンがあります.
    import { useState } from "react"
    
    export const Counter: React.FC = () => {
    
        const [count, setCount] = useState(0);
    
        return <div>
            <p>Counter value is {count}</p>
            <button onClick={() => {setCount(count + 1)}}>Increment</button>
            <button onClick={() => setCount(count - 1)}>Decrement</button>
            <button onClick={() => setCount(0)}>Reset</button>
        </div>;
    }
    
    以下のコードを考えてみましょう.setstate thriceを順番に呼び出し、コンソールで最後にcountを記録します.推測カウントの値は何ですか?
    import { useState } from "react"
    
    export const Counter: React.FC = () => {
    
        const [count, setCount] = useState(0);
    
        const handleIncrementThrice = () => {
            setCount(count + 1);
            setCount(count + 1);
            setCount(count + 1);
            console.log("count value "+count);
        }
    
        return <div>
            <p>Counter value is {count}</p>
            <button onClick={handleIncrementThrice}>Increment thrice</button>
        </div>;
    }
    
    インクリメントTHUREボタンをクリックすると、コンソールの値が0になりますが、コンソールに表示される値は3となります.
    setstateが非同期であるからではありません.それが非同期である理由は、反応が不必要な再レンダリングを回避しようとしているということです.
    反応は、カウント+ 1で3つのsetstateを受け取ります、そして、それはバッチにそれらをバッチして、3つのすべてが同じであることを確認します、そして、次に、SetStatus I . Eによってセットされる最新の値で1回だけ更新することによって、変更を効果的に作ります.実際に内部的に反応するのは以下の通りです:
    Object.assign({
     {}, 
     yourFirstCallToSetState,
     yourSecondCallToSetState,
     yourThirdCallToSetState
    });
    
    また、以下のコードを見て、setstateを1、2、3の順番でインクリメントします
    import { useState } from "react"
    
    export const Counter: React.FC = () => {
    
        const [count, setCount] = useState(0);
    
        const handleIncrementThrice = () => {
            setCount(count + 1);
            setCount(count + 2);
            setCount(count + 3);
        }
    
        return <div>
            <p>Counter value is {count}</p>
            <button onClick={handleIncrementThrice}>Increment thrice</button>
        </div>;
    }
    
    UIのカウントは3として値を取得し、初期状態0から6ではありません.それで、ちょうど我々がちょうど値で通過するとき、非同期setstate呼び出しを純粋にバッチして、反応して、最新の呼び出しでUIを更新するでしょう.
    それからすべての3つのsetstate呼び出しを実行する方法?
    ここでの事実は、setstateが関数を受け入れ、その関数が状態更新を実行し、新しい状態を返し、期待通り振舞うことです.それで、あなたがSetStateに機能を渡すとき、それは彼らの各々を通して演じます.
    import { useState } from "react"
    
    export const Counter: React.FC = () => {
    
        const [count, setCount] = useState(0);
    
        const handleIncrementThrice = () => {
            setCount((count) => count + 1);
            setCount((count) => count + 2);
            setCount((count) => count + 3);
        }
    
        return <div>
            <p>Counter value is {count}</p>
            <button onClick={handleIncrementThrice}>Increment thrice</button>
        </div>;
    }
    
    しかし、より便利な機能は、それはあなたの状態を更新する前にチェックを課すようないくつかのプログラムの制御を与えることです.このインクリメントメソッドをアプリケーションの他の場所にも使用する場合は、共通の共有ファイルに移動できます.したがって、コンポーネントのクラスとは別に状態変化を宣言できます.
    function incrementByValue(count: number, incValue: number): number {
        if(count > 10) return count; 
            return count + incValue;
    }
    
    パターンと反パターン
    1 .状態はプライベートデータとするべきです.あなたはそのコンポーネントのためにそれを必要とするか、子供たちに小道具を通してそれを渡すことができます.しかし、どんなコンポーネントの外でも状態を変更することは、扱いにくいシナリオを除いて基本的に必要ではありません.
  • は、Renderメソッドで計算された値を取得しないではなく、派生値を返す際にあなたのために仕事をするメソッドまたは関数を記述します.単純な用語では、レンダリングメソッドを流さないでください.以下の例を考えてください.
  • type UserProps = {
        firstName: string;
        lastName: string;
    }
    
    export const User: React.FC<UserProps> = ({firstName, lastName}) => {
        // Do not do this
    
        // return <div>
            // <p>Full name is {firstName + ' ' + lastName}</p>
        // </div>;
    
        // Instead just derive in a variable or declare a method 
        // in cases where complex transformations are required
    
        const fullName = firstName + ' ' + lastName;
        return <div>
            <p>Full name is {fullName}</p>
        </div>;
    }
    
    3 .あなたがレンダリングしようとしないことのために状態を使用しないでください.
    コンポーネントの状態オブジェクト内の小文字を定義しないよう注意してください.
    使いやすいデフォルト値を使用します.
    例えば、あなたのAPIが配列を返します
    デフォルトの状態を配列とする必要があります.さもなければ、そのAPI呼び出しが我々が考えたものより長くなるつもりであるならば、何が起こるかは、混乱を引き起こすでしょう.
    州建築様式
    通常、反応状態は、コンポーネントに格納され、子プロセスに小道具として渡されます.私たちは、実際の状態を個人として考慮する必要があります、我々はすべての子供たちにそれを示すことができます、しかし、我々がそれを変える必要があるならば、すべては州が定められた場所に戻る必要があります.
    データダウン.イベントアップ
    状態に基づいて何かをレンダリングするすべてのコンポーネントを識別します.次に、共通の所有者(階層内の状態を必要とするすべてのコンポーネントの上に1つのコンポーネント)を検索します.
    階層内の共通の所有者または他のコンポーネントのいずれかが状態を所有する必要があります.あなたが状態を所有するために意味をなすコンポーネントを見つけることができないならば、単に状態を保持するために単に新しいコンポーネントを作成してください、そして、それは一般の所有者構成要素より上に階層のどこかにそれを持ちます.ここで、コンポーネントの全体のアイデアは、単に状態を保持することです.
    つの異なるパターン
    コンテナパターンによる吊上げ状態
    Containerパターンは、状態とプレゼンテーションの間に線を描画します.プレゼンテーションコンポーネントは小道具を受け取り、UIをレンダリングします.これは非常に簡単にテストするには、我々は単位テストを書いている、我々はちょうどプレゼンテーションの層に小道具を渡すことができますし、コンポーネントが予想通りにレンダリングされているかどうかをチェックすることができます.
    それは状態を持ち上げるだけでなく、コンテナもデータ取出しの責任を持ちます.それで、基本的な考えはあなたのプレゼンテーション・コンポーネントdumbを共有して、再利用するのを簡単にして、それのために単位テストを書くことを作ることです.
    プレゼンテーションコンポーネントは、アクションを受け取り、コンテナに戻します.アクションは、いくつかの状態を更新する必要がある場合など、コールバック関数として機能するプレゼンテーション層から起動することができます.
    したがって、簡単なことに、カウンター機能を考慮すると、カウンターと呼ばれる一つのステートレスコンポーネントとカウンターコンテナと呼ばれる別のステートフルコンポーネントがあります.
    プレゼンテーション層用コード
    // PRESENTATION LAYER
    
    export type CounterProps = {
      count: number; // state from container
      onIncrement(): void; // actions from container 
      onDecrement(): void;
      onReset(): void;
    };
    
    export const Counter: React.FC<CounterProps> = ({
      count,
      onIncrement,
      onDecrement,
      onReset,
    }) => {
      return (
        <>
          <p>Counter value is {count}</p>
          <button onClick={onIncrement}>Increment</button>
          <button onClick={onDecrement}>Decrement</button>
          <button onClick={onReset}>Reset</button>
        </>
      );
    };
    
    状態を保持するコンテナコンポーネント:
    import { Counter } from "./Counter";
    import { useState } from "react";
    
    export const CounterContainer: React.FC = () => {
      const [count, setCount] = useState(0);
    
      // callback to update state in container
      const handleIncrement = () => {
        setCount(count + 1);
      };
    
      const handleDecrement = () => {
        setCount(count - 1);
      };
    
      const handleReset = () => {
        setCount(0);
      };
    
      return (
        <Counter
          count={count}
          onIncrement={handleIncrement}
          onDecrement={handleDecrement}
          onReset={handleReset}
        />
      );
    };
    
    コンテナパターンのトレードオフ
    それでも、容器パターンがプレゼンテーション・レイヤーを分離することにより多くの柔軟性を与えるとしても、それはまだ小道具をドリルダウンさせなければなりません、そして、あなたは不必要な再レンダリングを防ぐことができません.
    不要なレンダリングを防ぐためにusecallbackとusememoを使用することができます.