React.memo 컴포넌트 리렌더링 방지

Doum Kim·2020년 5월 14일
2

React - 기초

목록 보기
15/20
post-thumbnail

컴포넌트가 동일한 props로 동일한 결과를 렌더링 한다면 React.memo를 호출하여 결과를 저장하여 불필요한 렌더링을 방지할 수 있다.
React.memo는 props 변화에만 영향을 준다. state나 context가 변할 때는 렌더링이된다. 오직 성능 최적화를 위해 사용해야한다.

CreateUser.jsx

import React from "react";

const CreateUser = ({ username, age, onCreate, onChange }) => {
  console.log("createUser 렌더링");
  return (
    <>
      <input onChange={onChange} value={username} name="username" type="text" />
      <input onChange={onChange} value={age} name="age" type="number" />
      <button onClick={onCreate}>추가</button>
    </>
  );
};

export default React.memo(CreateUser);

UserList.jsx

import React from "react";

const User = React.memo(({ user, onRemove, onToggle }) => {
  console.log("User 렌더링");
  return (
    <>
      <div>
        <span
          style={{
            cursor: "pointer",
            color: user.active ? "red" : "#000"
          }}
          onClick={() => {
            onToggle(user.id);
          }}
        >
          username: {user.username}, age: {user.age}{" "}
        </span>
        <button
          onClick={() => {
            onRemove(user.id);
          }}
        >
          삭제
        </button>
      </div>
    </>
  );
});

const UserList = ({ users, onRemove, onToggle }) => {
  console.log("UserList 렌더링");
  return (
    <>
      {users.map(user => {
        return (
          <User
            user={user}
            key={user.id}
            onRemove={onRemove}
            onToggle={onToggle}
          />
        );
      })}
    </>
  );
};

export default React.memo(UserList);

그렇지만 useCallback의 deps로 넣어 준 users 배열이 계속 바뀌기 때문에 onCreate, onRemove, onToggle이 새로 만들어진다.
따라서 불필요한 렌더링이 발생한다. 여기서 사용할 방법은 함수형 업데이트다. 함수형 업데이트를하면 최신 users를 참조 할 수 있기 때문에
deps가 불필요해진다.

import React, { useState, useRef, useMemo, useCallback } from "react";
import UserList from "./UserList";
import CreateUser from "./CreateUser";

const countActiveUser = users => {
  console.log("카운트");
  return users.filter(user => user.active).length;
};

export default function App() {
  const [inputs, setInputs] = useState({
    username: "",
    age: ""
  });
  const { username, age } = inputs;

  const [users, setUsers] = useState([
    {
      id: 1,
      username: "Kim",
      age: 20
    },
    {
      id: 2,
      username: "Lee",
      age: "30"
    },
    {
      id: 3,
      username: "Choi",
      age: "40"
    }
  ]);

  const nextId = useRef(4);

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

  const onCreate = useCallback(() => {
    const user = {
      username,
      age,
      id: nextId.current,
      active: false
    };
    setUsers(users => [...users, user]);
    setInputs({
      username: "",
      age: ""
    });
    nextId.current += 1;
  }, [username, age]);

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

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

  const count = useMemo(() => countActiveUser(users), [users]);

  return (
    <div>
      <CreateUser
        username={username}
        age={age}
        onChange={onInputChange}
        onCreate={onCreate}
      />
      <div>activeUser : {count}</div>
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
    </div>
  );
}

0개의 댓글