useState 함수형 업데이트

박종한·2020년 4월 1일
0

React

목록 보기
16/17

이틀 전에 썼던 useState()함수형 업데이트의 추가적인 부분을 적고자 한다.

그를 위해선 먼저 useCallback, useMemo 등을 이용한 최적화에 대해서 먼저 이해하고 있어야 한다.
https://velog.io/@johnque/useMemo-Hook
https://velog.io/@johnque/useCallback-Hook

두 최적화를 진행해주고 나서도 문제는, App에 사용된 각종 함수들(onCreate, onChange, onRemove, onToggle 등)안의 users가 업데이트가 되기 때문에, 자식들인 다양한 애들이 따라서 업데이트가 되버린다.

  const onCreate = useCallback(() => {
    const user = {
      id: nextId.current,
      height,
      weight
    }
    setUsers(users.concat(user));
    nextId.current += 1;
    setInputs({
      height: '',
      weight: ''
    });
  }, [height, weight, users]);

  const onChange = useCallback(e => {
    const { name, value } = e.target;
    setInputs({
      ...inputs,
      [name]: value
    });
  }, [inputs]);

  const onToggle = useCallback(id => {
    setUsers(
      users.map(
        user =>
          user.id === id
            ? { ...user, active: !user.active }
            : user
      )
    );
  }, [users]);

  const onRemove = useCallback(id => {
    setUsers(users.filter(user => user.id !== id));
  }, [users]);

이런식으로 App에 작성되어 있다고 해보자.
각자 state의 변경이 일어나기 때문에, App.js의 자식 컴포넌트인 각종 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({
    height: '',
    weight: ''
  });

  const { height, weight } = inputs;
  const [users, setUsers] = useState([
    {
      id: 1,
      height: 185,
      weight: 75,
      active: false,
    },
    {
      id: 2,
      height: 167,
      weight: 50,
      active: false,
    },
    {
      id: 3,
      height: 173,
      weight: 80,
      active: false,
    }
  ]);

  const nextId = useRef(4);

  const onCreate = useCallback(() => {
    const user = {
      id: nextId.current,
      height,
      weight
    }
    setUsers(users => users.concat(user));
    nextId.current += 1;
    setInputs({
      height: '',
      weight: ''
    });
  }, [height, weight]);

  const onChange = useCallback(e => {
    const { name, value } = e.target;
    setInputs(inputs => ({
      ...inputs,
      [name]: value
    }));
  }, []);

  const onToggle = useCallback(id => {
    setUsers(users =>
      users.map(
        user =>
          user.id === id
            ? { ...user, active: !user.active }
            : user
      )
    );
  }, []);

  const onRemove = useCallback(id => {
    setUsers(users => users.filter(user => user.id !== id));
  }, []);

  const count = useMemo(() => countActiveUsers(users), [users]);
  return (
    <>
      <CreateUser
        height={height}
        weight={weight}
        onCreate={onCreate}
        onChange={onChange}
      />
      <UserList users={users} onToggle={onToggle} onRemove={onRemove} />
      <div>
        <b>활성 사용자 수 :</b>{count}
      </div>
    </>
  );
}

export default App;

이런식으로 함수형 업데이트를 진행해주면 App이 바뀌어도, callback측에서는 []안에 바뀐 state들이 들어있지 않으므로, 리렌더링을 진행하지 않는다.

따라서, 리렌더링 횟수를 줄이기 위해서 함수형 업데이트를 하는 것이다.

profile
코딩의 고수가 되고 싶은 종한이

0개의 댓글