본문 바로가기

TIL

TIL - [React] 리렌더링 동작을 제어하는 역할 useEffect, useRef

오늘은 useEffectuseRef는 리렌더링 동작을 제어하는 역할을 하는 훅인데 어떤 상황에서 써야 하는지 자세히 알아봤습니다.

 

 

useEffect

useEffect 훅은 함수 컴포넌트에서 사이드 이펙트를 수행하기 위해 사용됩니다.

 

사용 하는 경우

  1. 데이터 페칭: 컴포넌트가 마운트 될 때 서버로부터 데이터를 가져오는 경우
  2. 구독/해제: 웹 소켓이나 이벤트 리스너를 설정하고 정리하는 경우
  3. DOM 업데이트: 특정 DOM 요소의 속성을 직접 변경하는 경우
  4. 타이머 설정: setTimeout, setInterval과 같이 타이머를 설정하는 경우

useEffect는 컴포넌트가 마운트 될 때와 의존성 배열에 있는 값이 변경될 때 마다 실행되고 클린업 함수가 포함되어 있는 경우 컴포넌트가 언마운트 되거나 의존성이 변경되기 전에 실행됩니다

 

예시

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

function ExampleComponent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  useEffect(() => {
    console.log('Count has changed:', count);

    // 클린업 함수
    return () => {
      console.log('Cleaning up...');
    };
  }, [count]);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
      />
    </div>
  );
}

 

 

useEffect는 다음과 같은 상황에 실행 됩니다.

  1. 컴포넌트 마운트 시: 처음으로 컴포넌트가 화면에 렌더링 될 때
  2. count값이 변경될 때: 버튼을 클릭해서 count 값을 증가시킬 때

클린업 함수는 count 값이 변경되어 useEffect가 다시 실행되기 전에 이전 효과를 정리하는 용도로 사용됩니다.

 

다른 예시

// 의존성 배열이 없는 경우
useEffect(() => {
  console.log('렌더링 될 때마다 계속 실행됩니다.');
});

// 빈 의존성 배열 
useEffect(() => {
  console.log('오직 처음에 렌더링 된 이후 한 번만 실행됩니다.');

  return () => {
    console.log('컴포넌트가 언마운트 될 때 한 번만 실행됩니다.');
  };
}, []);

// 여러 의존성
useEffect(() => {
  console.log('둘 중 하나라도 변경되면 실행 됩니다', count, text);
}, [count, text]);

 

 

useRef

useRef 훅은 컴포넌트 생애 주기 전반에 걸쳐 유지되는 변경 가능한 참조를 제공하기 위해 사용됩니다.

 

사용하는 경우

  1. DOM 요소 접근: 특정 DOM 요소에 직접 접근해야 할 때
  2. 값 유지: 렌더링 간에 값이 유지되어야 하지만, 해당 값이 변경될 때 리렌더링이 필요 업슨 경우
  3. 타이머 저장: setTimeout이나 setInterval의 ID를 저장하는 경우

useRef는 리렌더링을 트리거하지 않고 current 프로퍼티를 통해 값을 저장하고 접근할 수 있습니다.

useRef의 current 프로퍼티는 상태가 변경되어도 컴포넌트가 리렌더링되지 않기 때문에 성능 최적화에 유리합니다

 

예시

// DOM 요소 접근하기
import React, { useRef } from 'react';

function FocusInput() {
  const inputRef = useRef(null);

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>Focus the input</button> 
    </div>
  );
}

useRef를 사용하여 입력 필드에 대한 참조를 생성하고, 버튼 클릭 시 해당 입력 필드에 포커스를 줍니다.

 

// 컴포넌트의 이전 값 저장하기
import React, { useState, useEffect, useRef } from 'react';

function PreviousValueExample() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();

  useEffect(() => {
    prevCountRef.current = count;
  }, [count]);

  const prevCount = prevCountRef.current;

  return (
    <div>
      <p>Current Count: {count}</p>
      <p>Previous Count: {prevCount}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

useRef와 useEffect를 사용하여 이전 count 값을 저장하고, 이를 현재 값과 비교할 수 있습니다.

 

// 데이터 유지하기
import React, { useRef } from 'react';

function FormComponent() {
  const formDataRef = useRef({
    name: '',
    email: ''
  });

  const handleChange = (e) => {
    formDataRef.current[e.target.name] = e.target.value;
  };

  const handleSubmit = () => {
    console.log(formDataRef.current);
  };

  return (
    <div>
      <input name="name" onChange={handleChange} />
      <input name="email" onChange={handleChange} />
      <button onClick={handleSubmit}>Submit</button>
    </div>
  );
}

useRef를 사용하여 폼 데이터를 유지할 수 있습니다. 리렌더링 조건 중 State를 사용하지 않아서 입력 값이 변경될 때마다 컴포넌트가 리렌더링되지 않습니다.

 

요약 - useEffect와 useRef가 사용 하는 경우

useEffect

  • 사이드 이펙트를 수행해야 하는 경우 (데이터 페칭, 이벤트 리스너 설정, 타이머 설정)
  • 컴포넌트가 마운트될 때나 의존성이 변경 될 때 특정 작업을 수행해야 하는 경우
  • 컴포넌트가 언마운트될 때 클린업 작업이 필요한 경우

useRef

  • 렌더링 간에 값을 유지하지만, 해당 값이 변경될 때 리렌더링이 필요하지 않을 경우
  • DOM 요소에 직접 접근해야 하는 경우
  • 타이머 ID와 같은 참조를 저장해야 하는 경우

결론

특정 작업이 컴포넌트의 생애주기와 관련된 경우 useEffect를 사용하고 리렌더링과 무관하게 DOM에 접근할 필요가 있는 경우에 useRef를 사용합니다.