[React-hooks] useMemo & useCallback

Updated:

useMemo

useMemo는 memoized(메모이제이션)된 값을 반환함.

  • memoized : 컴퓨터 프로그램이 동일한 계산을 반복 해야할때 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게하는 기술. 메모아이제이션이라고도 함.

useMemo는 의존성이 변경되었을 때에만 메모이제이션도니 값만 다시 계산함, 이 최적화는 모든 렌더링 시의 고비용 계산을 방지하게 해줌.

  • useMemo로 전달된 함수는 렌더링 중에 실행된다. 통상적으로 렌더링 중에는 하지 않는 것을 이 함수 내에서 하지않아야함. 예를들어 사이드 이펙트(side effct)는 useEffect에서 하는 일이지 useMemo에서 하는일이 아님

  • 배열이 없는 경우 매 렌더링 때마다 새 값을 계산함.

  • useMemo는 성능 최적화를 위해 사용할 수는 있지만 의미상으로 보장이 있다고 생각하면 안됨.

사용법

전체 코드중 부분..


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: 'public.velopert@gmail.com',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@example.com',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: 'liz@example.com',
      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
      )
    );
  };
  
  //useMemo
  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;


useMemo의 첫번째 파라미터에는 어떻게 연산할지 정의하는 함수를 넣어주고 두번째 파라미턴에는 deps 배열을 넣어주면 되는데, 이 배열의 내용이 바뀌면 등록한 함수를 호출해서 값을 연산 해주고, 만약 내용이 바뀌지 않았다면 이전에 연산한 값을 재사용하게 됨.

useCallback

const memoizedCallback = useCallback(
    () => {
        doSomething(a, b);
    },
    [a ,b],
);

memoized 된 콜백을 반환함

인라인 콜백과, 의존성 값의 배열을 전달하면 useCallback은 콜백의 memoized 된 버전을 반환함. memoized 된 버전은 콜백의 의존성이 변경되었을때만 변경됨. 이것은 불필요한 렌더링을 방지하기 위해 쓰임 (예 shouldComponentUpdate를 사용하여 ) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트 에 콜백을 전달할때 유용함.

useCallback(fn, deps)useMemo(() => fn, deps), 와 같다.

예제

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: 'public.velopert@gmail.com',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@example.com',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: 'liz@example.com',
      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 onToggle = useMemo(
//       () => () => {
//         /* ... */
//       },
//       [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;


출처

Tags:

Categories:

Updated:

Leave a comment