反応を続けるHook#10


#10 Hook種類が多すぎうううう
userMemoの使用方法
パフォーマンスを最適化するために、usemoというhookを使用して計算値を再使用します.
AppコンポーネントにcountActiveUsersという関数を作成し、active値がtrueのユーザー数を数えてスクリーンにレンダリングします.
App.js
import React, { useRef, useState } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';

function countActiveUsers(users) {
  console.log('활성 사용자 수를 세는중...');
  return users.filter(user => user.active).length;
}

function App() {
  const [inputs, setInputs] = useState({
    username: '',
    email: ''
  });
  const { username, email } = inputs;
  const onChange = e => {
    const { name, value } = e.target;
    setInputs({
      ...inputs,
      [name]: value
    });
  };
  const [users, setUsers] = useState([
    {
      id: 1,
      username: 'velopert',
      email: '[email protected]',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: '[email protected]',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: '[email protected]',
      active: false
    }
  ]);

  const nextId = useRef(4);
  const onCreate = () => {
    const user = {
      id: nextId.current,
      username,
      email
    };
    setUsers(users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  };

  const onRemove = id => {
    // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
    // = user.id 가 id 인 것을 제거함
    setUsers(users.filter(user => user.id !== id));
  };
  const onToggle = id => {
    setUsers(
      users.map(user =>
        user.id === id ? { ...user, active: !user.active } : user
      )
    );
  };
  const count = countActiveUsers(users);
  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
      <div>활성사용자 수 : {count}</div>
    </>
  );
}

export default App;
countActiveUsers関数がコンソールにメッセージを出力する理由は、関数を呼び出すたびに、私たちがそれを知りたいからです.

アクティブなユーザー数は、別のアカウント名をクリックして緑に変更することで更新されます.

しかし、ここには問題が発生しています.すなわち,inputの値が変化するとcountActiveUsers関数も呼び出される.

アクティブなユーザの数を計算する場合は、ユーザが変化した場合にのみ使用する必要がありますが、入力値が変化するたびに素子が再レンダリングされるため、不要な呼び出しが行われています.
この場合、usemoというhook関数を使用してパフォーマンスを最適化します.
Memoは「memorized」を表し、以前に計算した値を再使用することを表します.
APPファイルを次のように変更します.
App.js
import React, { useRef, useState, useMemo } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';

function countActiveUsers(users) {
  console.log('활성 사용자 수를 세는중...');
  return users.filter(user => user.active).length;
}

function App() {
  const [inputs, setInputs] = useState({
    username: '',
    email: ''
  });
  const { username, email } = inputs;
  const onChange = e => {
    const { name, value } = e.target;
    setInputs({
      ...inputs,
      [name]: value
    });
  };
  const [users, setUsers] = useState([
    {
      id: 1,
      username: 'velopert',
      email: '[email protected]',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: '[email protected]',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: '[email protected]',
      active: false
    }
  ]);

  const nextId = useRef(4);
  const onCreate = () => {
    const user = {
      id: nextId.current,
      username,
      email
    };
    setUsers(users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  };

  const onRemove = id => {
    // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
    // = user.id 가 id 인 것을 제거함
    setUsers(users.filter(user => user.id !== id));
  };
  const onToggle = id => {
    setUsers(
      users.map(user =>
        user.id === id ? { ...user, active: !user.active } : user
      )
    );
  };
  const count = useMemo(() => countActiveUsers(users), [users]);
  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
      <div>활성사용자 수 : {count}</div>
    </>
  );
}

export default App;
userMemoはuserEffectに似たパラメータを持ち,1つ目はどのように演算するかを定義する関数を加え,2つ目はdeps配列を加える.
この配列に配置されたコンテンツが変更された場合、登録された関数を呼び出して値を計算し、コンテンツが変更されていない場合は、以前に計算された値を再使用します.
結果は...

input値が入力されてもコンソールには何の変化もないので完了します.
useCallbackの使用
useCallbackはuseMemoに似たHookです.
userMemoは、特定の結果値を再使用するために使用されます.
useCallbackは、特定の関数を再作成したくない場合に使用します.
Appファイルで実装されるonCreate、onRemove、onToggle関数を見てみましょう.
const onCreate = () => {
  const user = {
    id: nextId.current,
    username,
    email
  };
  setUsers(users.concat(user));

  setInputs({
    username: '',
    email: ''
  });
  nextId.current += 1;
};

const onRemove = id => {
  // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
  // = user.id 가 id 인 것을 제거함
  setUsers(users.filter(user => user.id !== id));
};
const onToggle = id => {
  setUsers(
    users.map(user =>
      user.id === id ? { ...user, active: !user.active } : user
    )
  );
};
上記の関数は、構成部品を再レンダリングするたびに再作成されます.
関数を再宣言することは、CPUが大量のリソースを消費するという意味ではありませんが、関数を一度作成する必要がある場合にのみ再作成および再使用することが重要です.
この場合、useCallbackを使用できます.
App.js
import React, { useRef, useState, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';

function countActiveUsers(users) {
  console.log('활성 사용자 수를 세는중...');
  return users.filter(user => user.active).length;
}

function App() {
  const [inputs, setInputs] = useState({
    username: '',
    email: ''
  });
  const { username, email } = inputs;
  const onChange = useCallback(
    e => {
      const { name, value } = e.target;
      setInputs({
        ...inputs,
        [name]: value
      });
    },
    [inputs]
  );
  const [users, setUsers] = useState([
    {
      id: 1,
      username: 'velopert',
      email: '[email protected]',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: '[email protected]',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: '[email protected]',
      active: false
    }
  ]);

  const nextId = useRef(4);
  const onCreate = useCallback(() => {
    const user = {
      id: nextId.current,
      username,
      email
    };
    setUsers(users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  }, [users, username, email]);

  const onRemove = useCallback(
    id => {
      // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
      // = user.id 가 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]
  );
  const count = useMemo(() => countActiveUsers(users), [users]);
  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
      <div>활성사용자 수 : {count}</div>
    </>
  );
}

export default App;
関数に使用状態またはpropsがある場合はdeps配列に含める必要があります.
deps配列に関数で使用される値を入れないと、関数でこれらの値を参照するときに最新の値が参照されることは保証されません.
propsが受け取った関数があればdepsにも入れるべきです.
useCallbackもこのように表現できます.
const onToggle = useMemo(
  () => () => {
    /* ... */
  },
  [users]
);
useCallbackを使用して明らかな最適化は行われていません.
パフォーマンスが最適化されるのは、コンポーネントレンダリングの最適化操作のみです.
注:ベロフォードとのモダン反応
感じ:
  • 今日はHookの2種類を紹介しました:useMemoとuseCallback.
  • は似たようなところがありますが、使い道が違うのでよく知ってから使います.