React.memo

G-NOTE·2021년 6월 7일
0

React

목록 보기
7/27
post-custom-banner
  • 리액트 최적화를 위해 사용한다. (컴포넌트의 리렌더링 방지)
  • 기존 함수(func)에 React.memo(func)로 감싼다.

CreateUser.js

import React from "react";

function CreateUser({ username, age, onChange, onCreate }) {
  return (
    <div>
      <input
        name="username"
        placeholder="계정명"
        onChange={onChange}
        value={username}
      />
      <input name="age" placeholder="나이" onChange={onChange} value={age} />
      <button onClick={onCreate}>등록</button>
    </div>
  );
}

export default React.memo(CreateUser);

UserList.js

import React, { useEffect } from "react";

const User = React.memo(function User({ user, onRemove, onToggle }) {
  const { username, age, id, active } = user;
  useEffect(() => {
    console.log("user값이 설정되었습니다.");
    console.log(user);
    return () => {
      console.log("user값이 바뀌기 전");
      console.log(user);
    };
  }, [user]);
  return (
    <div>
      <b
        onClick={() => {
          onToggle(id);
        }}
        style={{ color: active ? "red" : "black", cursor: "pointer" }}
      >
        name: {username}
      </b>
      <span> age: {age}</span>
      <button
        onClick={() => {
          onRemove(id);
        }}
      >
        Delete
      </button>
    </div>
  );
});

function UserList({ users, onRemove, onToggle }) {
  return (
    <div>
      {users.map((user) => (
        <User
          user={user}
          key={user.id}
          onRemove={onRemove}
          onToggle={onToggle}
        />
      ))}
    </div>
  );
}

export default React.memo(UserList);

App.js

import { 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: "",
    age: "",
  });

  const { username, age } = inputs;

  const [users, setUsers] = useState([
    { id: 1, username: "bae", age: 22, active: true },
    { id: 2, username: "lee", age: 21, active: false },
    { id: 3, username: "lim", age: 25, active: false },
  ]);

  const nextId = useRef(4);

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

  const onCreate = useCallback(() => {
    // 새로운 user 객체 생성
    const user = {
      id: nextId.current,
      username,
      age,
    };

    setUsers([...users, user]);

    setInputs({
      username: "",
      age: "",
    });

    nextId.current += 1;
  }, [users, username, age]);

  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]
  );

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

  return (
    <>
      <CreateUser
        username={username}
        age={age}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
      <div>활성 사용자 수 : {count}</div>
    </>
  );
}
export default App;
  • onCreate, onRemove, onToggle 모두 users를 deps로 갖고 있기 때문에 users가 바뀌면 리렌더링 된다.
  • 불필요한 리렌더링을 방지하려면 위 함수들이 기존 users를 참조하면 안된다.

(수정) App.js

  const onCreate = useCallback(() => {
    // 새로운 user 객체 생성
    const user = {
      id: nextId.current,
      username,
      age,
    };

    // setUsers([...users, user]);
    setUsers((users) => users.concat(user));

    setInputs({
      username: "",
      age: "",
    });

    nextId.current += 1;
  }, [username, age]);
  • setUsers에서 users를 매개변수로 받기 때문에 deps에서 username과 age만 받는다.

(수정) App.js

  const onRemove = useCallback((id) => {
    setUsers((users) => users.filter((user) => user.id !== id));
  }, []);
  • onRemove 함수는 컴포넌트가 처음 렌더링될 때 한번 만들어지고 이후 계속 재사용된다.

(수정) App.js

  const onToggle = useCallback((id) => {
    setUsers((users) =>
      users.map((user) =>
        user.id === id ? { ...user, active: !user.active } : user
      )
    );
  }, []);
  • onToggle 함수 또한 컴포넌트가 처음 렌더링될 때 한번 만들어지고 이후 계속 재사용된다.

완성

App.js

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

  const { username, age } = inputs;

  const [users, setUsers] = useState([
    { id: 1, username: "bae", age: 22, active: true },
    { id: 2, username: "lee", age: 21, active: false },
    { id: 3, username: "lim", age: 25, active: false },
  ]);

  const nextId = useRef(4);

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

  const onCreate = useCallback(() => {
    // 새로운 user 객체 생성
    const user = {
      id: nextId.current,
      username,
      age,
    };

    // setUsers([...users, user]);
    setUsers((users) => users.concat(user));

    setInputs({
      username: "",
      age: "",
    });

    nextId.current += 1;
  }, [username, age]);

  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
      )
    );
  }, []);

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

  return (
    <>
      <CreateUser
        username={username}
        age={age}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
      <div>활성 사용자 수 : {count}</div>
    </>
  );
}

export default App;
profile
FE Developer
post-custom-banner

0개의 댓글