React useRef Hook

김명원·2024년 12월 26일
0

learnReact

목록 보기
8/26

🛠️ useRef Hook 기초

useRef렌더링에 필요하지 않은 값을 참조할 수 있도록 도와주는 React Hook입니다.
DOM을 직접 조작하거나, 상태(state)와는 별개로 값을 저장할 때 유용하게 사용됩니다.

const ref = useRef(initialValue);

useRef는 단일 프로퍼티인 current를 가진 객체를 반환합니다.


🔍 useRefuseState의 차이점

  • useRef: 값이 변경되어도 리렌더링이 발생하지 않음.
  • useState: 값이 변경되면 컴포넌트가 리렌더링됨.

예제: useRef와 리렌더링

import { useRef } from "react";

export default function AppRef() {
  const countRef = useRef(0);

  console.log("리렌더링!");

  const handleClick = () => {
    countRef.current++;
    console.log(countRef.current); // 값이 업데이트되지만 리렌더링은 발생하지 않음
  };

  return (
    <>
      <h2>Count</h2>
      <button onClick={handleClick}>Count</button>
    </>
  );
}

useRef의 리렌더링이 console 안되는 결과


예제: useState와 리렌더링

import { useRef, useState } from "react";

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

  console.log("리렌더링!");

  const handleClick = () => {
    setCount(count + 1); // 값이 업데이트되며 리렌더링 발생
    console.log(count);
  };

  return (
    <>
      <h2>Count</h2>
      <button onClick={handleClick}>Count</button>
    </>
  );
}

useState의 리렌더링이 console되는 결과


🔑 useRef와 일반 변수(let)의 차이점

  • useRef: 컴포넌트가 리렌더링되어도 값이 초기화되지 않음.
  • let: 리렌더링될 때마다 값이 초기화됨.

코드 예시

import { useRef, useState } from "react";

export default function AppRef() {
  const countRef = useRef(0);
  const [count, setCount] = useState(0);

  let counter = 0;

  console.log("리렌더링!");

  const handleClick = () => {
    counter++; // 리렌더링 시 값 초기화
    countRef.current++; // 리렌더링 시 값 유지
    setCount(count + 1); // 리렌더링 발생
    console.log("counter:", counter, "countRef:", countRef.current);
  };

  return (
    <>
      <h2>Count</h2>
      <button onClick={handleClick}>Count</button>
    </>
  );
}


🧾 Ref의 특징과 한계

특징

1️⃣ 리렌더링 사이 정보 저장

  • 리렌더링이 발생해도 값을 유지.

2️⃣ 리렌더링 비발생

  • 값을 변경해도 컴포넌트가 리렌더링되지 않음.

3️⃣ 로컬 저장

  • 컴포넌트 간 공유되지 않고, 해당 컴포넌트 내부에서만 값 저장.

한계

useRef는 화면에 표시되는 정보를 저장하기에는 부적합합니다.
이런 경우에는 useState를 사용하는 것이 더 적합합니다.


🎯 Ref의 주요 사용 사례

useRef는 주로 DOM 조작에 사용됩니다.
다음 예제에서는 useRef를 사용하여 DOM에 직접 접근하고 포커스를 관리합니다.


🛠️ 실습: DOM 접근 및 폼 제어

예제: Ref를 사용한 버튼 클릭 수 카운터

import { useRef, useState } from "react";

function ButtonCounter() {
  const countRef = useRef(0);

  const handleClick = () => {
    countRef.current++; // 리렌더링 발생 없이 값 증가
    console.log("CountRef:", countRef.current);
  };

  return <button onClick={handleClick}>Click Me</button>;
}

export default ButtonCounter;

📌 결과

  • 버튼을 클릭할 때마다 countRef의 값이 증가합니다.
  • 하지만 화면에는 아무런 변화가 없으며 리렌더링도 발생하지 않습니다.

예제: Ref를 사용한 폼 입력 관리

import { useRef, useState } from "react";

function Form() {
  const [form, setForm] = useState({
    title: "",
    author: "",
    content: "",
  });

  const titleInputRef = useRef(null);
  const authorInputRef = useRef(null);
  const contentTextareaRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!form.title) {
      titleInputRef.current.focus(); // 제목 입력란으로 포커스 이동
    } else if (!form.author) {
      authorInputRef.current.focus(); // 작성자 입력란으로 포커스 이동
    } else if (!form.content) {
      contentTextareaRef.current.focus(); // 내용 입력란으로 포커스 이동
    } else {
      console.log("폼 제출 성공!", form);
    }
  };

  const handleChange = (e) => {
    const { name, value } = e.target;
    setForm((prev) => ({
      ...prev,
      [name]: value,
    }));
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        ref={titleInputRef}
        name="title"
        placeholder="제목"
        value={form.title}
        onChange={handleChange}
      />
      <input
        ref={authorInputRef}
        name="author"
        placeholder="작성자"
        value={form.author}
        onChange={handleChange}
      />
      <textarea
        ref={contentTextareaRef}
        name="content"
        placeholder="내용"
        value={form.content}
        onChange={handleChange}
      />
      <button type="submit">제출</button>
    </form>
  );
}

export default Form;

📌 결과

  • 각 필드가 비어 있을 경우, 해당 입력란으로 포커스가 이동합니다.
  • Ref를 활용하여 DOM 요소에 직접 접근해 편리하게 폼 검증과 제어가 가능해집니다.

Ref 활용의 실질적 예시: 초점 관리와 DOM 조작

  1. 입력 필드 포커스
    • 폼 작성 중 특정 조건에 따라 자동으로 입력 필드로 초점 이동.
  2. 애니메이션 및 스타일 변경
    • 특정 DOM 요소의 스타일 변경이나 애니메이션 트리거.

🛠️ 실습: App 구성

전체 앱을 구성하여 useRef의 사용 사례를 다양하게 보여줍니다.

import ButtonCounter from "./ButtonCounter";
import Form from "./Form";

export default function AppRef() {
  return (
    <>
      <h2>Button Counter</h2>
      <ButtonCounter />
      <h2>Form</h2>
      <Form />
    </>
  );
}

이 앱은 카운터 버튼폼 입력 관리를 포함하며, useRef의 다양한 활용을 체험할 수 있습니다.


⚡ useRef Hook 고급 활용

useRef는 기본적으로 DOM 접근과 상태 관리를 보조하는 데 사용됩니다. 이번 섹션에서는 useRef를 고급스럽게 활용하는 방법과 주요 예제를 살펴봅니다.


🔍 코드 분석: 고급 실습

예제: 폼 데이터 관리 및 변경 감지

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

function MyForm() {
  const [form, setForm] = useState({
    title: "제목",
    author: "명코딩",
    content: "",
  });

  const titleInputRef = useRef(null);
  const authorInputRef = useRef(null);
  const contentTextareaRef = useRef(null);

  const [isChanged, setIsChanged] = useState(false);
  const prevForm = useRef(null);

  useEffect(() => {
    prevForm.current = { ...form }; // 이전 폼 데이터를 저장
  }, []);

  useEffect(() => {
    // 이전 폼 데이터와 현재 폼 데이터를 비교해 변경 여부 확인
    const hasChanged =
      prevForm.current.title !== form.title ||
      prevForm.current.author !== form.author ||
      prevForm.current.content !== form.content;
    setIsChanged(hasChanged);
  }, [form]);

  const handleForm = (e) => {
    const { name, value } = e.target;
    setForm({
      ...form,
      [name]: value,
    });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!form.title) {
      titleInputRef.current.focus();
      return;
    }
    if (!form.author) {
      authorInputRef.current.focus();
      return;
    }
    if (!form.content) {
      contentTextareaRef.current.focus();
      return;
    }
    console.log("저장 성공!", form);
  };

  return (
    <form onSubmit={handleSubmit}>
      <fieldset>
        <legend>글 쓰기</legend>
        <input
          ref={titleInputRef}
          name="title"
          placeholder="제목"
          value={form.title}
          onChange={handleForm}
        />
        <hr />
        <input
          ref={authorInputRef}
          name="author"
          placeholder="작성자"
          value={form.author}
          onChange={handleForm}
        />
        <hr />
        <textarea
          ref={contentTextareaRef}
          name="content"
          placeholder="내용"
          value={form.content}
          onChange={handleForm}
        />
        <hr />
        <button disabled={!isChanged}>전송</button>
      </fieldset>
    </form>
  );
}

export default MyForm;

📝 주요 코드 분석 및 설명

  1. 폼 데이터 관리

    • useState를 활용하여 폼 데이터를 관리합니다.
    • setForm 함수를 통해 입력값을 업데이트합니다.
  2. useRef로 DOM 제어

    • titleInputRef, authorInputRef, contentTextareaRef를 사용해 DOM 요소에 직접 접근합니다.
    • 폼 필드 값이 비어 있으면 해당 필드로 자동으로 포커스를 이동시킵니다.
  3. 폼 변경 감지

    • prevForm.current에 이전 폼 데이터를 저장합니다.
    • useEffect를 통해 현재 폼 데이터와 이전 데이터를 비교하여 변경 여부를 확인합니다.
  4. 제출 버튼 상태

    • isChanged 상태를 활용해 폼 데이터가 변경된 경우에만 "전송" 버튼이 활성화됩니다.

🛠️ 커스텀 컴포넌트에서의 Ref 사용

기본적으로 React의 커스텀 컴포넌트는 내부 DOM 노드에 대한 ref를 외부로 노출하지 않습니다.
이를 해결하기 위해 forwardRef를 사용하여 컴포넌트에 ref를 전달할 수 있습니다.

예제: forwardRef를 사용한 폼 컴포넌트

import { forwardRef, useRef } from "react";

const MyForm = forwardRef((props, ref) => {
  return (
    <form ref={ref}>
      <fieldset>
        <legend>글 쓰기</legend>
        <input placeholder="제목" />
        <hr />
        <input placeholder="작성자" />
        <hr />
        <textarea placeholder="내용" />
        <hr />
        <button>전송</button>
      </fieldset>
    </form>
  );
});

export default MyForm;

부모 컴포넌트에서 사용하기

import { useRef } from "react";
import MyForm from "./MyForm";

export default function AppRef() {
  const formRef = useRef(null);

  const handleFocus = () => {
    console.log("Form Reference:", formRef.current);
  };

  return (
    <>
      <MyForm ref={formRef} />
      <button onClick={handleFocus}>폼 참조 확인</button>
    </>
  );
}

📝 주요 코드 분석 및 설명

  1. forwardRef로 ref 전달

    • forwardRef를 사용하여 부모 컴포넌트에서 자식 컴포넌트의 DOM 참조를 사용할 수 있습니다.
  2. 부모 컴포넌트에서 DOM 조작

    • formRef.current를 통해 자식 컴포넌트의 DOM 요소에 접근 가능합니다.

💡 활용 사례 및 추가 실습

1️⃣ 다중 컴포넌트 상태 동기화

  • useRef를 사용하여 다중 컴포넌트 간 상태를 동기화하거나 특정 값을 공유할 수 있습니다.

2️⃣ 애니메이션 제어

  • DOM 요소에 직접 접근하여 애니메이션 시작이나 종료를 제어할 수 있습니다.

profile
개발자가 되고 싶은 정치학도생의 기술 블로그

0개의 댓글