Frontend/React

5. Hooks

예린lynn 2024. 8. 1. 06:34
728x90

 

state와 Lifecycle은 Class Component에만 사용 가능하고, Function Component에서는 사용 불가하다. 

이때 Function Component에서 이런 기능을 대신 지원하는 것이 바로 Hooks이다.

참고로 Hooks의 모든 이름은 use로 시작한다.

 

 

Hook 규칙

1. Hook은 무조건 최상위 레벨에서만 호출해야 한다.

   -> 반복문이나 조건문 또는 중첩된 함수들 안에서 훅을 호출x

   -> Hook은 컴포넌트가 렌더링 될 때마다 매번 같은 순서로 호출되어야 한다.

 

2. 리액트 함수 컴포넌트에서만 Hook을 호출해야 한다.

 

  cf) eslint-plugin-react-hooks

    -> 훅의 규칙을 따르도록 강제해주는 플러그인.

    -> 리액트 컴포넌트가 훅의 규칙을 따르는지 아닌지 분석하고 경고 표시를 해준다.

 

useState()

  • state를 사용하기 위한 Hook
  • 가장 대표적이고 많이 사용된다.
  • 변수 각각에 대해 set 함수가 따로 존재한다.
//기본형
const [변수명, set함수명] = useState(초기값);

//예시
import React, { useState } from "react";

function Counter(props) {
  const [count, setCount] = useState(0);
  
  return (
    <div>
    	<p>총 {count}번 클릭했습니다.</p>
		<button onClick={() => setCount(count + 1)}>
          클릭
		</button>
	</div>
  );
}

 

 

useEffect()

  • Lifecycle과 동일한 기능
  • Side effect(효과, 영향)을 수행하기 위한 Hook
  • Side Effect는 다른 컴포넌트에 영향을 미칠 수 있으며, 렌더링 중에는 작업이 완료될 수 없다.
  • 이펙트 함수 : 처음 컴포넌트가 렌더링된 이후와, 업데이트로 인한 재렌더링 이후 실행
  • 의존성 배열 : 이펙트가 의존하고 있는 배열. 배열 안의 변수가 하나라도 값이 변경되면 이펙스 함수 실행
//기본형
useEffect(이펙트 함수, 의존성 배열);

//이펙트 함수가 mount, unmount 시에 단 한 번씩만 실행되는 경우
userEffect(이펙트 함수, []);

//컴포넌트가 업데이트 될 때마다 호출
useEffect(이펙트 함수);

 

- 예시

 

1. componenetDidMount, componenetDidUpdate 기능

import React, { useState , useEffect } from "react";

function Counter(props) {
  const [count, setCount] = useState(0);
  
  // componenetDidMount, componenetDidUpdate와 비슷하게 작동합니다.
  useEffect(() => {
    //브라우저 API를 사용해서 document의 title을 업데이트 합니다.
    document.title = 'You clicke ${count} times':
  });
  
  return (
    <div>
    	<p>총 {count}번 클릭했습니다.</p>
		<button onClick={() => setCount(count + 1)}>
          클릭
		</button>
	</div>
  );
}

 

2. componentWillUnmount와 동일한 기능

  -> useEffect()의 return 함수의 역할

import React, { useState , useEffect } from "react";

function UserStatus(props) {
  const [isOnline, setIsOnline] = useState(null);
  
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  
  useEffect(() => {
    ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
    return () => { //
      ServerAPI.unsubscibeUserStatus(props.user.id, handleStatusChange);
    }; //컴포넌트가 unmount 될 때 호출됨!
  });
  
  if (isOnline == null) {
    return '대기 중...';
  }
  return isOnline ? '온라인' : '오프라인';
}

 

 

useMemo()

  • Memoized value를 리턴하는 Hook
  • Memoization : 비용이 높고 연산 양이 많은 함수 호출 결과를 저장해 두었다가, 같은 입력값으로 함수 호출 시 이전에 저장한 호출 결과를 바로 반환하는 것. 불필요한 중복 연산이 없기 때문에 최적화에 좋다.
  • Memoized value : Memoization이 된 결과 값
  • 의존성 배열에 들어있는 변수가 변했을 경우에만 새로 create 함수를 호출하여 결과값을 반환하며, 그렇지 않을 경우 기존 함수 결과값을 반환한다.
  • 렌더링이 일어나는 동안 함수를 실행한다.
const memoizedValue = useMemo(
  () => {
    //연산량이 높은 작업을 수행하여 결과를 반환
    return computeExpensiveValue(의존성 변수1, 의존성 변수2);
  },
  [의존성 변수1, 의존성 변수2]
);

 

 

useCallback()

  • useMemo()와 유사하지만 값이 아닌 함수를 반환한다.
  • 의존성 배열에 있는 변수 중 하나라도 변경되면 memoization된 callback 함수를 반환한다.
const memoizedCallback = useCallback(
  () => {
    doSomething(의존성 변수1, 의존성 변수2);
  },
  [의존성 변수1, 의존성 변수2]
);

 

 

useRef()

  • Reference를 반환하기 위한 Hook
  • Refernece : 특정 컴포넌트에 접근할 수 있는 객체
  • 파라미터로 초깃값을 넣으면 해당 초깃값으로 초기화된 reference 객체를 반환한다.
  • 반환된 reference 객체는 컴포넌트의 lifetime 전체에 걸쳐서 유지된다.
  • 내부의 데이터가 변경되었을 때 별도로 알리지 않는다. (current 속성이 변경되어도 재렌더링 발생x)
//기본값
const refContainer = useRef(초깃값);

//예시
function TextInputWithFocusButton(props) {
  const inputElem = useRef(null);
  
  const onButtonClick = () => {
    //'current'는 마운트된 input element를 가리킴
    inputElem.current.focus();
  };
  
  return (
    <>
    	<input ref={inputElem} type="text" />
    	<button onClick={onButtonClick}>
          Focus the input
        </button>
	</>
  );
}

 

 

cf) Callback ref

  • useRef()는 내부 데이터가 변경되어도 알 수 없기 때문에, 노드의 변화를 알기 위해 사용하는 방법이다.
  • 다른 노드에 연결될 때마다 callback을 호출한다.
  •  자식 컴포넌트가 변경되었을 때 알림을 받을 수 있고 이를 통해 다른 정보들을 업데이트할 수 있다.
function MeasureExample(props) {
  const [height, setHeight] = useState(0);
  
  const measureRef = useCallback(node => {
    if (node != null) {
      setHeight(node.getBountingClientRect().height);
    }
  }, []);
 
  return (
    <>
    	<h1 ref={measureRef}>안녕, 리액트<h1>
    	<h2>위 헤더의 높이는 {Math.round(height)}px 입니다.</h2>
    </>
  );
}

 

 

Custom Hook 만들기

  • 기본적으로 제공되는 훅들 이외에 추가적으로 필요한 기능이 있다면 직접 훅 생성 가능
  • 여러 컴포넌트에서 반복적으로 사용되는 로직을 훅으로 만들어 재사용할 수 있다.
  • Custom Hook의 이름은 꼭 use로 시작해야 한다.
  • 여러 개의 컴포넌트에서 하나의 Custom Hook을 사용할 때 컴포넌트 내부에 있는 모든 state와 effects는 전부 분리되어 있다.
  • 리액트 컴포넌트는 각각의 Custom Hook 호출에 대해서 분리된 state를 얻게 된다.

 

728x90