React Hooks

김내현·2024년 11월 26일

개인공부

목록 보기
32/51

1. React Hooks 소개

  • 등장 배경: React Hooks는 2018년 React 16.8 버전에서 도입되었다[1][2]. 함수형 컴포넌트에서도 상태 관리와 생명주기 메서드를 사용할 수 있게 해주는 기능이다.
  • 클래스 컴포넌트의 한계: 클래스 컴포넌트에서의 this 사용의 복잡성, 코드 재사용의 어려움 등을 해결하기 위해 도입되었다[1].

2. Hooks의 기본 원리

  • 함수형 컴포넌트와 Hooks: Hooks는 함수형 컴포넌트에서 상태와 다른 React 기능을 사용할 수 있게 해준다[3].
  • Hook 규칙:
    • Hooks는 React 함수 컴포넌트 내에서만 호출할 수 있다.
    • 조건문이나 반복문 내부에서 호출할 수 없다[3].

3. 주요 Hook 소개

useState

상태를 선언하고 업데이트하는 데 사용된다.
state의 생성과 동시에 가져야할 초기값을 useState()에 인자로 넣어주면 state와 setState 두가지 요소를 배열 형태로 리턴 해준다.const [state,setState] = useState(초기값);
현재 상태 값은 state에 들어 있고 state를 변경 주고 싶을 때는 setState 함수를 이용해서 변경시켜 줄 수 있다.
setState()를 사용해서 state를 변경하면 해당 컴포넌트 화면에 다시 랜더링이 된다.
state: 컴포넌트가 가질 수 있는 상태를 말한다.

useState의 동작 원리
상태와 상태 업데이트 함수: useState는 배열을 반환하며, 첫 번째 요소는 현재 상태, 두 번째 요소는 상태를 업데이트하는 함수입니다. 예를 들어, const [value, setValue] = useState(initialState);와 같이 사용한다.
상태 업데이트 함수의 형태: setValue와 같은 상태 업데이트 함수는 두 가지 방식으로 호출할 수 있다.
직접 값으로 설정: setValue(newValue)처럼 새로운 값을 직접 전달.
콜백 함수를 사용하여 설정: setValue(prevValue => newValue)처럼 이전 값을 인자로 받아 새로운 값을 계산.

  • 예제: 숫자 카운터 구현.
import { useState } from "react";

const UseState = () => {
  const [value, setValue] = useState(0);
  console.log(value);
  return (
    <div>
      <p>
        현재 카운터 값은 <b>{value}</b> 입니다.
      </p>
      <button onClick={() => setValue(value + 1)}>+1</button>
    </div>
  );
};

export default UseState;
  • 예제: 팀원 추가.
import { useState } from "react";

const UseStateCallback = () => {
  const [names, setNames] = useState([
    "김세빈",
    "김예빈",
    "임승현",
    "최보아",
    "최민혁",
  ]);
  const [input, setInput] = useState("");

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setInput(e.target.value);
  };

  const handleUpload = () => {
    setNames((prevState) => {
      return [input, ...prevState];
    });
  };
  return (
    <div>
      <input type="text" value={input} onChange={handleInputChange} />
      <button onClick={handleUpload}>upload</button>
      {names.map((name, idx) => {
        return <p key={idx}>{name}</p>;
      })}
    </div>
  );
};

export default UseStateCallback;
  • useEffect
    어떤 컴포넌트가 mount(화면에 첫 랜더링)되었을 때, update(다시 랜더링)될 때, unmount(화면에서 사라질 때, mount 해제 되었을 때)되었을 때 특정 작업을 처리할 코드를 실행주고 싶을 때 사용
    기본적으로 useEffect 훅은 인자로 콜백함수를 받는다. 콜백 함수 내부에 작업을 처리할 코드를 작성 해주면 된다.
  1. 인자로 콜백함수만 받는 경우
    컴포넌트가 랜더링이 될 때마다 실행
    콜백은 컴포넌트가 화면에 랜더링된 직후에 불리는 것

  2. 콜백함수와 두번째 인자로 배열(dependency array)을 받는 경우
    컴포넌트가 맨 처음 화면에 랜더링이 될때와 배열안의 값이 바뀔 때 실행

clean up
콜백함수의 리턴 값은 컴포넌트가 언마운트 될 때(화면에서 사라질 때) 실행이 된다.
예를 들어 구독 설정 및 해제 등이 있습니다.

  • 예제: 타이머.
import React, { useState, useEffect } from "react";
import Timer from "./Timer";

const UseEffectCleanUp = () => {
  const [showTimer, setShowTimer] = useState(false);
  return (
    <div>
      {showTimer && <Timer />}
      <button onClick={() => setShowTimer(!showTimer)}>Toggle Timer</button>
    </div>
  );
};

export default UseEffectCleanUp;

import { useEffect } from "react";

export default function Timer() {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log("타이머 돌아가는 중...");
    }, 1000);

    return () => {
      clearInterval(timer);
      console.log("타이머가 종료됨");
    };
  }, []);
  return (
    <div>
      <span>타이머 시작 콘솔 확인</span>
    </div>
  );
}
  • 예제: 숫자 카운터 구현.
import React, { useState, useEffect } from "react";
const UseEffect = () => {
  const [count, setCount] = useState(1);
  const [name, setName] = useState("");

  const handleCountUpdate = () => {
    setCount(count + 1);
  };

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setName(e.target.value);
  };

  useEffect(() => {
    console.log("Effect 실행됨");
  });

  useEffect(() => {
    console.log("count 변화");
  }, [count]);

  useEffect(() => {
    console.log("name 변화");
  }, [name]);

  useEffect(() => {
    console.log("mounting");
  }, []);

  return (
    <div>
      <button onClick={handleCountUpdate}>증가</button>
      <span>count: {count}</span>
      <input type="text" value="" onChange={handleInputChange} />
      <span>name: {name}</span>
    </div>
  );
};
export default UseEffect;
  • useRef
    useRef()를 하면 ref 오브젝트 반환 const ref =useRef(value) -> {current:value}
    ref 오브젝트 수정 가능, 반환된 ref는 컴포넌트의 전 생애주기를 통해 유지가 된다.
    컴포넌트가 계속 랜더링이 되어도 언마운트 되기 전까지는 겂을 그대로 유지할 수 있다.
    useRef 사용이 유용한 대표적인 상황 두가지
  1. 저장공간
    ref는 state와 비슷하게 어떤 값을 저장해 두는 저장공간으로 사용된다.
    state의 변화 -> 랜더링 -> 컴포넌트 내부 변수들 초기화 (원하지 않는 랜더링으로 곤란한 상황이 생길 수 있음)
    ref의 변화 -> !랜더링 -> 변수들의 값 유지됨
    state의 변화 -> 랜더링 -> 그래도 ref 값은 유지됨
  • 예제: 숫자 카운터 구현.
import React, { useState, useRef } from "react";

export default function UseRef() {
  const [count, setCount] = useState(0);
  const countRef = useRef(0);
  let countVal = 0;
  console.log(countRef);
  console.log("랜더링");
  const increaseCountState = () => {
    setCount(count + 1);
  };
  const increaseRef = () => {
    countRef.current++;
    console.log("ref:", countRef.current);
  };
  const increaseVal = () => {
    countVal++;
    console.log("val:", countVal);
  };
  return (
    <div>
      <p>val: {countVal}</p>
      <p>state: {count}</p>
      <p>ref: {countRef.current}</p>
      <button onClick={increaseCountState}>state++</button>
      <button onClick={increaseRef}>ref++</button>
      <button onClick={increaseVal}>val++</button>
    </div>
  );
}
  1. DOM 요소에 접근
    ex) 로그인 화면에서 아이디 텍스트 박스에 클릭하지 않아도 자동으로 포커스 (querySeletor 같음)
import { useEffect, useRef } from "react";

export default function UseRefDOM() {
  const inputRef = useRef<HTMLInputElement>(null); // HTMLInputElement 타입 명시
  useEffect(() => {
    console.log();
    if (inputRef.current) {
      // current가 null이 아닌 경우에만 focus 호출
      inputRef.current.focus();
    }
  }, []);
  return (
    <div>
      <input ref={inputRef} type="text" placeholder="id" />
      <button>로그인</button>
    </div>
  );
}

4. Custom Hooks

  • 재사용 가능한 로직: Custom Hook을 사용하여 여러 컴포넌트에서 재사용 가능한 로직을 추출할 수 있습니다[3].
  • 예제: 쓰로틀링.
import { useRef } from "react";

export function useThrottle(callback: () => void, delay: number) {
  const lastRun = useRef(Date.now());
  return () => {
    const timeElapsed = Date.now() - lastRun.current;

    if (timeElapsed >= delay) {
      callback();
      lastRun.current = Date.now();
    }
  };
}


// 사용 예시
import { useRef } from "react";

export function useThrottle(callback: () => void, delay: number) {
  const lastRun = useRef(Date.now());
  return () => {
    const timeElapsed = Date.now() - lastRun.current;

    if (timeElapsed >= delay) {
      callback();
      lastRun.current = Date.now();
    }
  };
}

5. Hooks의 장점과 한계

  • 장점:
    • 코드 가독성과 유지보수성 향상[1].
    • 상태 관련 로직의 재사용성 증가.
  • 한계:
    • 초기 학습 곡선이 있을 수 있음.
    • 모든 상황에 적합하지 않을 수 있음(예: 복잡한 상태 관리).

결론

React Hooks는 함수형 컴포넌트를 더욱 강력하게 만들어주는 도구로, 현대적인 React 개발에 필수적인 요소이다.

Citations:
[1] https://www.youtube.com/watch?v=qjEcsNYFWYg
[2] https://velog.io/@velopert/react-hooks
[3] https://www.w3schools.com/react/react_hooks.asp
[4] https://hewonjeong.github.io/deep-dive-how-do-react-hooks-really-work-ko/
[5] https://deview.kr/data/deview/session/attach/Recoil_%E1%84%8B%E1%85%AA%E1%86%BC%E1%84%8B%E1%85%B1%E1%84%85%E1%85%B3%E1%86%AF_%E1%84%80%E1%85%A8%E1%84%89%E1%85%B3%E1%86%BC%E1%84%8C%E1%85%AE%E1%86%BC.pdf
[6] https://www.geeksforgeeks.org/reactjs-hooks/
[7] https://www.freecodecamp.org/news/react-hooks-fundamentals/
[8] https://corner-ds.tistory.com/228
[9][useState](https://www.youtube.com/watch?v=G3qglTF-fFI&list=PLZ5oZ2KmQEYjwhSxjB_74PoU6pmFzgVMO&index=1&t=645s)
[10][useEffect](https://www.youtube.com/watch?v=kyodvzc5GHU&list=PLZ5oZ2KmQEYjwhSxjB_74PoU6pmFzgVMO&index=2)
[11][custom hook](https://www.youtube.com/watch?v=S6POUU2-tr8&list=PLZ5oZ2KmQEYjwhSxjB_74PoU6pmFzgVMO&index=10)

0개의 댓글