Group Study (2022-2023)/React.js

[React] 3주차 스터디 - (입문) React의 Hooks

smjan27 2022. 10. 28. 23:59

Hook이란?

  • 리액트의 state와 생명주기 기능에 갈고리를 걸어 원하는 시점에 정해진 함수를 실행할 수 있도록 만들어진 기능
  • 훅을 통해 함수 컴포넌트에서 클래스 컴포넌트의 기능을 동일하게 구현 가능
  • 이름은 모두 use로 시작

useState()

  • 함수 컴포넌트에서 state를 사용하기 위한 훅 (원래는 클래스 컴포넌트에서만 제공)
  • 변경하고자 하는 변수 각각에 대해 set 함수를 따로 생성

*사용법: const [변수명, set함수명] = useState(초깃값)

import { useState } from "react";

export default function Counter(props) {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>총 {count}번 클릭했습니다.</p>
      <button onClick={() => setCount(count + 1)}>클릭</button>
    </div>
  );
}

useEffect()

  • 사이드 이펙트(서버에서 데이터를 받아오거나 수동으로 DOM 변경 등)를 수행하기 위한 훅
  • 클래스 컴포넌트의 생명주기 함수와 동일한 기능 수행
  • 선언된 컴포넌트의 props와 state에 접근 가능

*사용법: useEffect(이펙트 함수, 의존성 배열)

  • 컴포넌트가 마운트 된 이후, 의존성 배열에 있는 변수들 중 하나라도 값이 변경되었을 때 실행됨
  • 의존성 배열에 빈 배열([])을 넣으면 마운트와 언마운트 사이에 1번씩만 실행됨
  • 의존성 배열 생략 시 컴포넌트 업데이트 시마다 호출됨
  • return 함수는 컴포넌트 마운트가 해제될 때 호출됨
import { useEffect, useState } from "react";

export default function Counter(props) {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `총 ${count}번 클릭했습니다`;
    return () => {
      // 컴포넌트가 마운트 해제되기 전에 실행됨
      // componentWillUmount() 함수 역할과 동일
    };
  });

  return (
    <div>
      <p>총 {count}번 클릭했습니다.</p>
      <button onClick={() => setCount(count + 1)}>클릭</button>
    </div>
  );
}

useMemo()

  • Memoized value(이미 저장된 함수의 호출 결과)를 리턴하는 훅
  • 연산량이 높은 작업이 매번 렌더링될 때마다 반복되는 것을 피하기 위해 사용
  • 렌더링이 일어나는 동안 실행

*사용법: const memoizedValue = useMemo(값 생성 함수, 의존성 배열)

  • 의존성 배열에 들어있는 변수가 변했을 경우에만 새로 값 생성 함수를 호출하여 결괏값 반환, 그렇지 않은 경우에는 기존 함수의 결괏값을 그대로 반환

useCallback()

  • useMemo() 훅과 유사하지만 값이 아닌 함수를 반환
  • 컴포넌트 내에 함수를 정의하면 매번 렌더링이 일어날 때마다 함수가 새로 정의됨 -> useCallback() 훅을 사용하여 불필요한 함수 재정의 작업을 없앰

*사용법: const memoizedCallback = useCallback(콜백 함수, 의존성 배열)

  • 의존성 배열에 들어있는 변수가 변했을 경우에만 콜백 함수를 다시 정의하여 리턴
import { useEffect, useState, useMemo, useCallback } from "react";

export default function Counter(props) {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `총 ${count}번 클릭했습니다`;
    return () => {
      // 컴포넌트가 마운트 해제되기 전에 실행됨
      // componentWillUmount() 함수 역할과 동일
    };
  });

  const memoizedValue = useMemo(() => {
    // 연산량이 높은 작업을 수행하여 결과를 반환
    computeExpensiveValue(a, b); // 의존성 변수 1, 의존성 변수 2
  }, [a, b]);

  const memoizedCallback = useCallback(() => {
    // 값이 아닌 함수를 반환
    // 특정 변수의 값이 변한 경우에만 함수를 다시 정의
    doSomething(의존성변수1, 의존성변수2);
  }, [의존성변수1, 의존성변수2]);

  return (
    <div>
      <p>총 {count}번 클릭했습니다.</p>
      <button onClick={() => setCount(count + 1)}>클릭</button>
    </div>
  );
}

useRef()

  • 레퍼런스(특정 컴포넌트에 접근 가능한 객체)를 사용하기 위한 훅
  • 매번 렌더링될 때마다 항상 같은 레퍼런스 객체를 반환

*사용법: const refContainer = useRef(초깃값)

import { useRef } from "react";

export default function TextInputWithFocusButton(props) {
  // .current 속성을 가진 레퍼런스 객체 반환
  // 현재 참조하고 있는 엘리먼트를 의미
  // 컴포넌트가 마운트 해제되기 전까지 게속 유지됨
  const inputElement = useRef(null);

  const onButtonClick = () => {
    // current = 마운트된 input element
    inputElement.current.focus();
  };

  return (
    <>
      <input ref={inputElement} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

 

Hook의 규칙

  • 무조건 최상위 레벨에서만 호출해야 함
    • 반복문, 조건문, 중첩된 함수 안에서 호출 불가
    • 컴포넌트가 렌더링될 때마다 매번 같은 순서로 호출되어야 함
  • 리액트 함수 컴포넌트에서만 호출해야 함
    • 리액트 함수 컴포넌트에서 호출하거나 직접 만든 커스텀 훅에서만 호출 가능

 

커스텀 Hook

  • 내부에서 다른 훅을 호출하는 하나의 자바스크립트 함수
  • 매개 변수(parameter)로 무엇을 받을지, 어떤 것을 리턴할 지 개발자가 직접 정할 수 있음
  • 중복되는 로직을 커스텀 훅으로 추출하여 재사용성 증대
  • 이름이 use로 시작하는 이유: 특정 함수의 내부에서 훅을 호출하는지 알 수 없기 때문에 훅의 규칙

ex) useCounter(): 초기 카운트 값을 파라미터로 받아 count라는 state를 생성하여 값을 제공하고 카운트 증가 및 감소를 편리하게 하는 커스텀 훅

import { useState } from "react";

function useCounter(initialValue) {
  const [count, setCount] = useState(initialValue);

  const increaseCount = () => setCount((count) => count + 1);
  const decreaseCount = () => setCount((count) => Math.max(count - 1, 0));

  return [count, increaseCount, decreaseCount];
}

export default useCounter;
const [count, increaseCount, decreaseCount] = setCounter(0); // 사용 예시