[패스트캠퍼스 수강 후기] 프론트엔드 인강 100% 환급 챌린지 26회차 미션 - 30강 React useCallback, React.Memo

3 minute read

useCallback

이전에 만들었던 함수를 새로 만들지 않고 재사용하는 방법에 대해서 알아보자. useMemo랑 비슷한데 함수를 위한 Hook이다. 코드로 알아봐보자.

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 =>{ //useCallback 감싸줌
    const { name, value } = e.target;
    setInputs({
      ...inputs,
      [name]: value
    });
  },[inputs])
  const [users, setUsers] = useState([
    {
        id: 1,
        username: 'bmyu',
        email: 'bomee88@naver.com',
        active: true,
    },
    {
        id: 2,
        username: 'tester',
        email: 'tester@example.com',
        active: false,
    },
    {
        id: 3,
        username: 'sample',
        email: 'sample@example.com',
        active: false,
    }
  ]);

  const nextId = useRef(4);

  const onCreate = useCallback(() => { //useCallback 감싸줌
    const user = {
      id: nextId.current,
      username,
      email,
    };
    setUsers(users.concat(user));
    setInputs({
      username: '',
      email:''
    })
    console.log(nextId.current);
    nextId.current += 1;
  },[user, username, email])

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

  const onToggle = useCallback(id => { //useCallback 감싸줌
    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;
 

우리가 이전섹션에서 useMemo를 사용해서 함수를 재사용 했었듯이 나머지 함수들인 onChange, onCreate, onremove, onToggle도 모두 useCallback으로 감싸준다.
그리고 deps배열 안에는 함수안에서 의존하고 있는 값들을 넣어줘야 한다. 그래야만 그 의존하고 있는 값이 업데이트 될때만 그 함수를 사용하고 그렇지 않을 경우에는 기존에 만든 함수를 재사용하게 된다.
이렇게 한다고 해서 일단 눈에 띄는 변화는 없다. 우리가 나중에 컴포넌트 리랜더링 성능 최적화를 해줘야지만 비로소 성능이 좋아지기 때문이다. 근데 그 작업을 하기전에 어떤 컴포넌트를 현재 리랜더링 되고 있는지 알기 위해서 ‘React Dev Tools’를 사용해보자. 구글에서 검색해서 웹스토어에서 설치하면 된다. 수강인증이미지
설치하고나면 위와 같이 개발자 도구에서 component라는 탭이 생긴걸 볼 수 있을 것이다.
여기에서 App쪽에서 톱니바퀴를 누르고 ‘Highlight updates when components render.’를 체크한다.

React.memo

이제 인풋에 계정명에 글씨를 써넣어보자. 인풋에 글씨를 써넣을때마다 밑에 users도 리랜더링 되는 것을 볼 수 있다. 이번엔 이것을 최적화 해보자.

CreateUser.js : 하단의 export부분만 변경

export default React.memo(CreateUser);

UserList.js

import React, { useEffect } from 'react';

const User = React.memo(function User({ user, onRemove, onToggle }){ //React.memo로 감싸줌.
    const {username, email, id, active} = user;
    useEffect(()=>{
        console.log('user 값이 설정됨');
        console.log(user);
        return () => {
            console.log('user 값이 바뀌기 전');
            console.log(user);
        }
    },[user]);

    return(
        <div>
            <b
             style=
            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 React.memo(UserList); //React.memo로 감싸줌

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: 'bmyu',
        email: 'bomee88@naver.com',
        active: true,
    },
    {
        id: 2,
        username: 'tester',
        email: 'tester@example.com',
        active: false,
    },
    {
        id: 3,
        username: 'sample',
        email: 'sample@example.com',
        active: false,
    }
  ]);

  const nextId = useRef(4);

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

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

여기서 좀 많은 수정이 있었는데 deps의 users부분을 모두 함수형 업데이트를 해준다. 왜냐면 users를 계속 참조하기때문에 모든 컴포넌트가 리랜더링되기때문이다. 아무튼 위와 같이 변경을 해주고, 아까 설치한 React Dev Tools로 확인하려는데.. 알아보니 이게 제대로 돌아가는게 아니더라.. localhost라서 계속 리액트로 만든 앱이 아니라고 떠서 dev tool을 제대로 사용할수 없었다. 그래서 제대로된 확인은 일단 보류한다.. 선생님 화면에서도 localhost인데 react메뉴가 잘 뜨는데 나는 자꾸 react로 만든 앱이 아니라며 제대로 구동되지 않는다.(구글링 해도 서버로 임시로 돌리라는데 서버에 어떠케 올리는가ㅠㅠ) 아무튼 이 React.memo가 잘 적용되서 성능최적화가 되었다고 치고 일단 넘어가ㅈㅏ..

오늘은 여기까지..
시청 영상 30강 19~20까지
수강인증이미지
수강인증이미지

프론트엔드 개발 올인원 패키지 with React Online. 👉 https://bit.ly/31Cf1hp

Comments