[React] useCallback - 함수 재사용

룽지·2022년 5월 30일
0

React

목록 보기
11/12

1. useCallback

  • useMemo와 비슷한 Hook
    • useMemo : 특정 결과값을 재사용할 때 사용
    • useCallback : 특정 함수를 새로 만들지 않고 재사용
const onCreate = () => {
    // input 값을 user에 대입
    const user = {
      id: nextId.current,
      username,
      email,
    }
    // users 배열에 user 추가 - concat 함수
    setUsers(users.concat(user))

    // input 값 초기화
    setInputs({
      username: '',
      email: '',
    })
    nextId.current += 1
  }

  const onRemove = (id) => {
    // user.id가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듦
    // user.id가 id 인 것을 제거
    setUsers(users.filter((user) => user.id !== id))
  }
  const onToggle = (id) => {
    setUsers(users.map((user) => (user.id === id ? { ...user, active: !user.active } : user)))
  }
  • onCreat, onRemove, onToggle 함수
    • 컴포넌트가 리렌더링될 때마다 새로 만들어짐
    • 한번 만든 함수를 필요할때만 새로 만들고 재사용하는 것은 중요
      • 나중에 컴포넌트에서 props가 바뀌지 않았으면 Virtual DOM에 새로 렌더링하는 것 조차 하지 않고 컴포넌트의 결과물을 재사용하는 최적화 작업 => 함수를 재사용하는 것이 필수
const onCreate = useCallback(() => {
    // input 값을 user에 대입
    const user = {
      id: nextId.current,
      username,
      email,
    }
    // users 배열에 user 추가 - concat 함수
    setUsers(users.concat(user))

    // input 값 초기화
    setInputs({
      username: '',
      email: '',
    })
    nextId.current += 1
  }, [users, username, email])

  const onRemove = useCallback(
    (id) => {
      // user.id가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듦
      // user.id가 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]
  )

주의할 점

  • 함수 안에서 사용하는 상태 혹은 props가 있으면, deps 배열 안에 꼭 포함시켜야 함
  • 만약 deps 배열 안에 값을 넣지 않는다면, 함수 내에서 해당 값들을 참조할 때 가장 최신 값을 참조할 것이라고 보장 못 함
  • props로 받아온 함수가 있다면 deps에 넣어야 함

전체 코드

import { useRef, useState, useMemo, useCallback } from 'react'
import { CreateUser } from './CreateUser'
import { UserList } from './UserList'

const countActiveUsers = (users) => {
  console.log('활성 사용자 수를 세는 중...')
  const count = users.filter((user) => user.active).length
  return count
}

export const Test = () => {
  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: 'velopert',
      email: 'public.velopert@gmail.com',
      active: true,
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@example.com',
      active: false,
    },
    {
      id: 3,
      username: 'liz',
      email: 'liz@example.com',
      active: false,
    },
  ])

  const nextId = useRef(4)
  const onCreate = useCallback(() => {
    // input 값을 user에 대입
    const user = {
      id: nextId.current,
      username,
      email,
    }
    // users 배열에 user 추가 - concat 함수
    setUsers(users.concat(user))

    // input 값 초기화
    setInputs({
      username: '',
      email: '',
    })
    nextId.current += 1
  }, [users, username, email])

  const onRemove = useCallback(
    (id) => {
      // user.id가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듦
      // user.id가 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>
    </>
  )
}

2. useCallback과 useMemo

  • useCallback은 useMemo를 기반으로 만들어짐
const onToggle = useMemo(
  () => () => {
    /* ... */
  },
  [users]
);
  • 이런식으로 표현 가능

3. 리렌더링 확인

  • input만 입력해도 하이라이트 된 부분이 리렌더링되는 것을 볼 수 있음
  • React.memo를 사용하여 컴포넌트 리렌더링을 방지할 수 있음

해당 내용은 다음 자료를 참고했습니다.
https://react.vlpt.us/basic/18-useCallback.html

0개의 댓글