ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • React - 컴포넌트 리렌더링 방지(React.memo)
    React 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;

    'React' 카테고리의 다른 글

    React - App 컴포넌트에서 useReducer사용  (0) 2020.09.11
    React - useReducer  (0) 2020.09.11
    React - 함수 재사용(useCallback)  (0) 2020.09.11
    React - 연산한 값 재사용(useMemo)  (0) 2020.09.11
    React - useEffect Hook  (0) 2020.09.11
Designed by Tistory.