[React] React Hook

wldud·2025년 11월 6일

React

목록 보기
8/8

React에서 가장 기본적인 훅인 useState 사용법

import { useState } from "react";

function App() {
  // useState => Hooks
  // useState는 리액트에서 가장 기본적인 훅(Hook)이며, 함수 컴포넌트에서도 가변적인 상태를 지닐 수 있게 해준다.
  // => 이 함수가 호추로디면 배열을 반환한다.
  // => 반환된 배열의 첫 번째 요소는 상태 값, 두 번째 요소는 상태 값을 설정하는 함수
  // => useState 함수의 파라미터(매개변수)에는 상태의 기본값, 초기값을 넣어 줍니다.

  const [value, setValue] = useState<number>(0);
  const [name, setName] = useState<string>(
    "빈 문자열로 할당하지 않은 name 상태값입니다."
  );
  const [nickname, setNickname] = useState<string>(
    "빈 문자열을 할당하지 않은 nickname 상태값입니다."
  );

  const increment = () => setValue(value + 1);
  const decrement = () => setValue(value - 1);

  const onChangeName = (event: React.ChangeEvent<HTMLInputElement>) =>
    setName(event.target.value);
  const onChangeNickname = (event: React.ChangeEvent<HTMLInputElement>) =>
    setNickname(event.target.value);
  return (
    <div>
      <p>
        현재 카운터 값은: <b>{value}</b>
      </p>
      <button onClick={increment}>1 증가</button>
      <button onClick={decrement}>1 감소</button>

      <div>
        <input type="text" value={name} onChange={onChangeName} />
        <input type="text" value={nickname} onChange={onChangeNickname} />
      </div>

      <div>
        <b>이름: {name}</b>
        <b>별명: {nickname}</b>
      </div>
    </div>
  );
}

export default App;

useEffect

import { useEffect, useState } from "react";

function App() {
  // useEffect
  // useEffect는 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook 입니다.

  // - 마운트가 될 때만, 최초 1회만 실행하고 싶을 떄
  // 마운트란, 리액트 DOM에 우리가 return 키워드 하단에 작성한 HTML, CSS 영역 즉, UI가 붙었을 때 => 우리가 HTML을 자바스크립트로 통제 가능할 때
  // useEffect에서 설정한 함수를 컴포넌트가 화면에 맨 처음 렌더링 될 때만 실행하고,
  // 업데이트 될 때는 실행하지 않으려면, 함수에 두 번째 파라미터(매개변수)로 빈 배열을 넣어주면 됩니다.

  // - 특정 값이 업데이트 될 때만 실행하고 싶을 때
  // useEffect를 사용할 때, 특정 값이 변경될 때만 호출하고 싶을 경우도 있습니다.
  // useEffect의 두 번째 파라미터(매개변수)로 전달되는 배열 안에 검사하고 싶은 값을 넣어주면 됩니다.

  const [name, setName] = useState<string>("");
  const [nickname, setNickname] = useState<string>("");

  useEffect(() => {
    // 해당 컴포넌트가 최초 렌더링이 될 때, useEffect가 실행이 되고,
    // 우리가 선언한 state 즉, 상태 값이 변화하더라도 useEffect가 실행되는 것으로 보아
    // state 즉, 상태 값이 변화하면 해당 컴포넌트는 재렌더링이 된다는 것을 알 수 있습니다.
    console.log("컴포넌트가 렌더링 될 때마다 특정 작업을 수행합니다.");
    console.log("name: ", name);
    console.log("nickname: ", nickname);
  });

  useEffect(() => {
    console.log("마운트가 될 떄만 수행합니다. - 최초 1회 실시");
    console.log("name: ", name);
    console.log("nickname: ", nickname);
  }, []);

  useEffect(() => {
    console.log("name이라는 상태 값이 변할 경우에만 수행합니다.");
    console.log("name: ", name);
    console.log("nickname: ", nickname);
  }, [name]);

  useEffect(() => {
    console.log("뒷 정리하기");
    console.log("update name: ", name);

    return () => {
      console.log("cleanup");
      console.log(name);
    };
  }, [name]);

  const onChangeName = (e: React.ChangeEvent<HTMLInputElement>) =>
    setName(e.target.value);
  const onChangeNickname = (e: React.ChangeEvent<HTMLInputElement>) =>
    setNickname(e.target.value);

  return (
    <div>
      <input type="text" value={name} onChange={onChangeName} />
      <input type="text" value={nickname} onChange={onChangeNickname} />

      <div>
        <b>이름: {name}</b>
        <b>별명: {nickname}</b>
      </div>
    </div>
  );
}

export default App;
import { useEffect, useState } from "react";

function Timer() {
  const [count, setCount] = useState<number>(0);

  useEffect(() => {
    const id = setInterval(() => {
      console.log("Inerval 실행됨");
      setCount((c) => c + 1);
    }, 1000);

    return () => {
      console.log("cleanup: 이전 타이머 제거됨");
      clearInterval(id);
    };
  }, []);

  return <div>카운트: {count}</div>;
}

export default function APP() {
  const [visible, setVisible] = useState<boolean>(true);

  return (
    <div>
      {visible && <Timer />}
      <button onClick={() => setVisible(!visible)}>
        {visible ? "숨기기" : "보이기"}
      </button>
    </div>
  );
}

useMemo & useCallback

import { useCallback, useMemo, useState } from "react";

const getAverage = (numbers: number[]) => {
  console.log("평균값을 계산 중입니다.");

  if (numbers.length === 0) return 0;

  const sum = numbers.reduce((acc, cur) => acc + cur);
  return sum / numbers.length;
};

function App() {
  // useCallback
  // useCallback은 useMemo와 상당히 비슷한 함수입니다. 주로 렌더링 성능을 최적화 하는 상황에서 사용합니다.
  // 이 훅(Hook)을 사용하면 만들어 놓았던 함수를 재사용할 수 있습니다.

  // useCallback은 첫 번째 파라미터에는 생성하고 싶은 함수를 넣고,
  // useCallback의 두 번째 파라미터에는 배열을 넣으면 됩니다.
  // 이 배열에는 어떤 값이 바뀌었을 때, 함수를 새로 생성해야 하는지 명시해야 합니다.

  // onChange처럼 비어 있는 배열을 넣게 되면 컴포넌트가 렌더링 될 때, 만들었던 함수를 계속해서 재사용하게 되며
  // onInsert처럼 배열 안에 number와 list를 넣게 되면, 인풋 내용이 바뀌거나 새로운 항목이 추가되었을 때
  // 새로 만들어진 함수를 사용하게 됩니다.

  const [list, setList] = useState<number[]>([]);
  const [number, setNumber] = useState<string>(""); // => 실제 input 태그에 입력된 숫자를 list 배열에 주입할 것이기 때문에
  // 상태 값 이름을 number로 지정. 단, input 태그에 입력된 값이기 때문에 데이터 타입은 string임.

  const onChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    setNumber(e.target.value);
  }, []); // 컴포넌트가 처음 렌더링 될 때만 함수를 생성한다.

  const onInsert = useCallback(() => {
    // concat: Array 인스턴스의 concat 함수는 두 개 이상의 배열을 병합하는 데 사용됩니다. .
    // 이 메서드는 기존 배열을 변경하지 않고, 새 배열을 반환
    // parseInt: 문자열 인자를 파싱하여 특정 진수(수의 진법 체계에서 기준이 되는 값)의 정수를 반환합니다.
    const newList = list.concat(parseInt(number));
    setList(newList); // number[]
    setNumber(""); // number 상태 값 초기화
  }, [number, list]);

  // useCallback은 첫 렌더링 때 한 번만 함수 onInsert를 생성합니다.
  // onInsert 안에서 사용하는 list, number는 초기값의 복사본으로 함수 안에 "닫혀(closed over)" 있습니다.
  // 이후 number나 list가 변경되어도, onInsert는 옛날 값을 계속 사용

  // 이게 클로저(closure) 문제

  // list가 변할 때마다 getAverage 함수가 호출되어서 average 값을 계산하여 화면에 보임
  const average = useMemo(() => getAverage(list), [list]);

  return (
    <div>
      <input type="text" value={number} onChange={onChange} />
      <button onClick={onInsert}>등록</button>

      <ul>
        {list.map((item: number, index: number) => {
          return <li key={index}>{item}</li>;
        })}
      </ul>

      <div>
        <b>평균 값: {average}</b>
      </div>
    </div>
  );
}

export default App;

useRef

import { useRef } from "react";

function App() {
  // useRef
  // useRef 훅(Hook)은 함수 컴포넌트에서 ref라는 속성을 쉽게 사용할 수 있도록 도와주는 도구입니다.
  // React의 useRef는 컴포넌트 내에서 변하지 않는 값을 유지하거나 DOM 요소에 직접 접근할 때 사용하는 훅(Hook) 입니다.
  // 다른 React Hook과 목적이 다릅니다.

  // useRef는 값을 저장하거나 DOM에 접근하기 위해 사용하는 객체(참조값)를 생성하는 Hook입니다.
  // 저장된 값은 컴포넌트가 리렌더링되어도 유지되면, 값이 변경되어도 리렌더링을 일으키지 않습니다.

  // ref 속성은 JSX, TSX에서 요소나 컴포넌트에 참조를 연결하는 역할
  // App 컴포넌트에서 등록 버튼을 눌렀을 때, 포커스가 인풋 태그 쪽으로 넘어가도록 코드를 작성해 보도록 하겠습니다.

  const inputElement = useRef<HTMLInputElement | null>(null);
  const fileInputRef = useRef<HTMLInputElement | null>(null);

  const handleClick = () => {
    // useRef 동작
    inputElement.current?.focus();
    fileInputRef.current?.click();
  };

  return (
    <div>
      <input type="text" ref={inputElement} />
      <input type="file" ref={fileInputRef} />
      <button onClick={handleClick}>등록</button>
    </div>
  );
}

export default App;

0개의 댓글