第5章-Optimize性能


概要


前回のレッスンでは、アレイレンダリングも行いました.これで終わりそうですが、実はちょっと問題があります.再レンダリングです.反応器は以下の4つの状況で素子を更新する.
  • 道具変換時
  • 状態遷移時
  • 親構成部品の再レンダリング時
  • this.forceUpdateを使用してレンダリングを強制トリガする場合
  • だから今の状況から見るとinputに値を入力するとAppですjsのinputsというステータス値が変更されます.これならアプリjsは再レンダリングされ、そのサブアイテムはCreateUserです.jsとUserList.jsでも再レンダリングが発生します.
    配列の値が100個または1000個以上あると、入力値を入力するたびに再読み込みされますが、再読み込みすると大きな負荷が発生するのではないでしょうか.だからこの授業では性能を最適化する方法を学びたいです.

    React Developer Tools


    Chrome Web Storeでは、レンダリングを直感的に見ることができるReact Developer Toolsが存在します.

    Chrome開発者ツールを開くと、新しいComponentsラベルとProfilerラベルが表示されます.その中でまずComponentsタブを見てみましょう.

    Components



    App.jsのステータスも表示され、Create UserをクリックするとAppが表示されます.jsからもらった道具も見えます.さあ.次に、設定アイコンをクリックして、次のようにコンポーネントの表示時のHighlight updatesを確認します.

    次にinputを入力します.何か手がかりはありませんか?これが更新が起こっている証拠です.すなわち、inputを入力すると、input状態を変更するしかありませんが、配列状態はレンダリングされます.😲😲

    Profiler


    Profilerを見ればもっと詳しく知ることができます...Inputを入力する前にstart profilingをクリックして入力します.

    入力が完了したらstopを押して終了します.それでは結果が出ます.参考までに、ユーザ名に111を入力した結果である.

    Inputを入力し、すべての場所に時差がありますが、再レンダリングが発生しているのがどうしても見えます.

    useEffect Hook


    userEffectという名前のhookを使用すると、マウント時(最初の出現時)、アンロード時(消失時)、更新時(特定のprops変更時)に特定のタスクを処理するように構成部品を設定できます.

    マウント


    ユーザコンポーネントがマウントされているとき、つまり最初の起動時にコンソールに出力されるコードです.
    // UserList.js
    function User({ user, onRemove, onToggle }) {
      useEffect(() => { // ✅
        console.log('컴포넌트가 화면에 나타남');
      }, []);
      
      return (
        <div>
          <b
            style={{
              color: user.active ? 'green' : 'black',
              cursor: 'pointer',
            }}
            onClick={() => onToggle(user.id)}
          >
            {user.username}
          </b>{' '}
          <span>({user.email})</span>
          <button onClick={() => onRemove(user.id)}>삭제</button>
        </div>
      );
    }
    全部で3つのuserがあるので、3回現れます.その後、inputを入力して配列を作成、変更、削除しても、コンソールは再出力されません.
    これにより、useEffectは、マウント時に1回のみ実行する場合に使用できます.主にこのような場合は以下のように多く使われます.
  • REST API要求
  • ライブラリ
  • を使用
  • 非同期タスク(setInterval,settimeout)
  • 受信
  • props値を素子の状態
  • に設定.

    インストールされていません


    コンポーネントが画面から消えたときにコンソール出力に追加されます.userを削除してみます.現れたのはいいですか?
    function User({ user, onRemove, onToggle }) {
      useEffect(() => {
        console.log('컴포넌트가 화면에 나타남');
        return () => { // ✅
          console.log('컴포넌트가 화면에서 사라짐');
        };
      }, []);
      
      ...(생략)...
    アンインストールの主な作業は次のとおりです.
  • 非同期終了(clearInterval,clearTimeout)
  • 削除
  • ライブラリインスタンス
  • 更新


    最後に、特定の値が更新されたときにのみレンダリングされるように設定できます.これまでuserEffectの2番目のパラメータとして,空の配列のみを入れると,今回は値を加える.
    次に、空の配列にuserを追加するコードを示します.
    useEffect(() => {
       console.log(user);   
    }, [user]); // ✅
    ユーザーが追加または変更されると、変更されたユーザー値がコンソールに書き込まれます.つまり、一部の値は更新後に実行されます.userが変更される前にuser値を確認したい場合は、終了関数を使用します.
    useEffect(() => {
      console.log('user 값이 설정됨');
      console.log(user); // 바뀐 이후의 값
      return () => {
        console.log('user 값이 바뀌기 전');
        console.log(user); // 바뀌기 전에 값
      };
    }, [user]);

    user値をクリックして変更すると、returnが先に実行され、更新後に上記のコードが実行されることがわかります.
    🚨 Effectに登録されている関数を使用してpropsで受信した値を参照するか、userStateで管理されている値を参照する場合は、変数を空の配列に入れる必要があります.このようにしてこそ、その値段に最新の値段がつく.また、エラーは発生しませんが、後で警告が出る可能性があるので、習慣的に入れておけば大丈夫です.

    参考にする。配列を省略すると?


    最後に省略配列はどうなりますか?インストール、アンインストール、または更新のたびに実行が続行されます.
    useEffect(() => {
      console.log(user);
    });
    構成部品のマウント後に呼び出され、ユーザー値が変更されたときに呼び出され、アンインストール時に呼び出されます.つまり、useEffectと書かれていないようなものです.

    useMemo Hook


    useMemo Hookを使用すると、レンダリング中に特定の値を変更した場合にのみ演算が実行され、必要な値が変更されていない場合は、以前に計算した結果を再使用できます.
    たとえば、アクティブなユーザー数をカウントする機能を作成するとします.
    // App.js
    function countActiveUser(users) {
      console.log('활성 사용자 수를 세는중...');
      return users.filter((user) => user.active).length;
    }
    
    (...생략...)
    
    const count = countActiveUser(users);
    
      return (
        <>
          <CreateUser
            username={username}
            email={email}
            onChange={onChange}
            onCreate={onCreate}
          />
          <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
          <div>활성 사용자 수: {count}</div>
        </>
      );
    userをクリックすると、activeが変化したことがわかります.これにより、activeユーザーの数が変化したことがわかります.しかし、問題があります.input状態が変化した場合もappです.jsは再レンダリングされるため、countActiveUserも再実行されます.ユーザ状態の変更時にcountActiveUserを実行させたいだけです.
    このときusemoを使えばいいです.useEffectと使い方の差は少ないでしょう?
    const count = useMemo(() => countActiveUser(users), [users]);

    useCallback Hook


    useCallbackはuseMemoとかなり似た関数です.このHookを使用すると、必要に応じてイベントハンドラ関数を実行できます.
    私たちが実装したonCreate、onRemove、onToggleなどの関数は、ユーザーが変更したときに再レンダリングするだけです.同様に、onChange関数はinputsの変更時に再レンダリングするだけです.
    使い方も簡単です.useCallbackで包み、2番目のパラメータdepsにこの関数が参照する状態値を入れればよい.
      const onChange = useCallback(e => {
        const { name, value } = e.target;
        setInputs({
          ...inputs,
          [name]: value,
        });
      }, [inputs]);
      
      
      const onCreate = useCallback(() => {
        const user = {
          id: nextId.current,
          username,
          email,
        };
        setUsers(users.concat(user));
    
        setInputs({
          username: '',
          email: '',
        });
        nextId.current += 1;
      }, [username, email, users]);
      
      
      const onRemove = useCallback((id) => {
        setUsers(users.filter((user) => user.id !== id));
      }, [users]);
    
    
      const onToggle = useCallback((id) => {
        setUsers(
          users.map((user) =>
            user.id === id ? { ...user, active: !user.active } : user
          )
        );
      }, [users]);
    入れ忘れたら…?🤔 この関数では、ステータス値またはpropsを参照すると、最新のステータスではなく、以前の構成部品が初めて作成されたときのステータスが参照されます.
    空の配列に何を入れるべきか分からない場合は、関数が参照する変数がデフォルトとみなされます.私も最初は混同していましたが、そう考えると、もっと理解しやすくなりました.

    React.memo


    最後のハイライトはReact回顧録です.これまでHookは基本的に解決してきたが、まだ解決していない部分は、親が再レンダリングすれば、子供も再レンダリングされるということだ.
    この部分はReactですmemoを使うと簡単に解決できます.使い方も簡単です.
    export default React.memo(CreateUser);
    export default React.memo(UserList);
    これは何の意味があるのでしょうか.propsが変わったときだけレンダリングされます.つまり、入力するたびにアプリケーションが適用されます.jsでは、入力ステータス値が変更され、Callbackのみを使用して入力とonChangeが再レンダリングされます.従って、UserListとUserはAppである.jsで変化しても反応します.memoのため、ユーザーに関連するアイテムが変化しない限り、レンダリングは発生しません.
    次はinputを入力し、開発者ツールで変化をテストした結果です.

    userの場合、全く変化が見られません.
    しかし、これまで未解決の問題があった.アクティブなプロパティを変更するには、ユーザー名をクリックします.ではonToggleも変わりますではpropsとして渡されるUserListやUserも変わります.つまり、プレイヤーを1人変えると、プレイヤーリストがすべて再レンダリングされるというでたらめな状況が発生します.
    また、onCreate関数によりCreateUser素子のユーザ値に影響を与えるという問題もある.

    注-prevaAreEqual


    React.memoを使用する場合は、prsAreEqualを2番目のパラメータとして使用できます.ここでは、フェースレンダリングではなく前後のツールをインポートして比較し、falseに戻ってレンダリングできます.
    export default React.memo(
      UserList,
      (prevProps, nextProps) => nextProps.users === prevProps.users
    );
    仮に、UserListの前のユーザが現在のユーザと比較して、異なる時点でしかレンダリングできないとする.

    useStateの関数式更新


    setuUsersで関数式更新を使用すると、古い値(users)を呼び出すことですぐに更新できます.したがって、ユーザーをuseCallbackに追加する必要はありません.
    const onCreate = useCallback(() => {
      const user = {
        id: nextId.current,
        username,
        email,
      };
      setUsers((users) => users.concat(user));
    
      setInputs({
        username: "",
        email: "",
      });
      nextId.current += 1;
    }, [username, email]);
    
    const onRemove = useCallback((id) => {
      setUsers((users) => users.filter((user) => user.id !== id));
    }, []);
    
    const onToggle = useCallback((id) => {
      setUsers((users) =>
        users.map((user) =>
          user.id === id ? { ...user, active: !user.active } : user
        )
      );
    }, []);
    要するに、特定のユーザーをクリックするだけで、そのユーザーのみが更新されていることを確認できます.

    ちょうど


    パフォーマンスの最適化はわかりましたが、難しすぎます...複雑すぎて、やるべきことがたくさんあります.感じられる.ただし、パフォーマンスの最適化は、パフォーマンスが遅くなったときに使用できます.これは必ず使うという意味ではありません.