React

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

Starters 2020. 9. 11. 13:29

컴포넌트의 리렌더링 성능 최적화

export default React.memo(UserList);

컴포넌트 내보낼 때 React.memo() 로 감싸주면 됨

 

CreateUsers도 마찬가지

 

-> UserList.js의 User 컴포넌트는 한 파일에 있는 또 다른 컴포넌트이기 때문에 함수 자체를 React.memo로 감싸준다

const User = React.memo(function User({ user, onRemove, onToggle }){
  const {username, email, id, active} = user;

  useEffect(() => {
    console.log(user);
    return () => {
      console.log('user 값이 바뀌기 전');
      console.log(user);
    }
  }, [user]);

  return (
    <div>
      <b style={{
        color: active ? 'green': 'black',
        cursor: 'pointer'
      }}
      onClick={() => onToggle(id)}
      >
        {username}
      </b>
      &nbsp;
      <span>({email})</span>
      <button onClick={() => onRemove(id)}>삭제</button>
    </div>
  );
});

 

- App.js -

App.js의 JSX 를 보면 onRemove() onToggle() 등이 바뀔 때, UserList.js의 내용과 User컴포넌트의 내용도 바뀌게 된다.

-> 입력할 때마다 모든 컴포넌트들 리렌더링 됨

-> 해결위해서 App.js의 onCreate(), onRemove(), onToggle() 함수가 기존 users를 참조하지 않게 해야한다.

=> useState의 함수형 업데이트 사용

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

    console.log(nextId.current);

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

이러면 deps에서도 users 쓸 필요 없이, username과 email이 바뀔 때만 onCreate함수 호출된다.

 

나머지도 동일한 방식으로

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

 

React.memo의 두번째 파라미터 사용

- UserList.js -

export default React.memo(UserList, (prevProps, nextProps) => nextProps.users === prevProps.users);

이전 props와 다음 props가 같다면 리렌더링 하지 않겠다는 뜻

 

이전 코드

- 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: 'starters',
      email: 'public.starters@gmail.com',
      active: true,
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@gmail.com',
      active: false,
    },
    {
      id: 3,
      username: 'jajajojo',
      email: 'jajajojo@gmail.com',
      active: false,
    }
  ]);

  const nextId = useRef(4);

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

    console.log(nextId.current);

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

  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}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
      <div>활성 사용자 수: {count}</div>
    </>
  );
}

export default App;

 

- CreateUser.js -

맨 아랫줄에 React.memo만 없음

 

- UserList.js -

import React, { useEffect } from 'react';

function User({ user, onRemove, onToggle }){
  const {username, email, id, active} = user;

  useEffect(() => {
    console.log(user);
    return () => {
      console.log('user 값이 바뀌기 전');
      console.log(user);
    }
  }, [user]);

  return (
    <div>
      <b style={{
        color: active ? 'green': 'black',
        cursor: 'pointer'
      }}
      onClick={() => onToggle(id)}
      >
        {username}
      </b>
      &nbsp;
      <span>({email})</span>
      <button onClick={() => onRemove(id)}>삭제</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 UserList;