본문 바로가기

React

[React] Throttling/ Debouncing

Throttling & Debouncing 

짧은 시간 간격으로 연속해서 이벤트가 발생했을 때 과도한 이벤트 핸들러 호출을 방지하는 기법

 

Throttling

짧은 시간 간격으로 연속해서 발생한 이벤트들을 일정시간 단위 (delay) 로 그룹화하여 처음 또는 마지막 이벤트 핸들러만 호출되도록 하는것.

주로 사용되는 예 : 무한스크롤

 

Debouncing

짧은 시간 간격으로 연속해서 이벤트가 발생하면 이벤트 핸들러를 호출하지 않다가 마지막 이벤트로부터 일정 시간(delay) 이 경과한 후에한번만 호출하도록 하는 것 

주로 사용되는 예 : 입력값 실시간 검색, 화면 resize 이벤트

 

메모리 누수(Memory Leak)

필요하지 않은 메모리를 계속 점유하고 있는 현상

setTimeout 이 메모리 누수를 유발하는지?

- 하나의 페이지에서 페이지 이동없이 setTimeout 을 동작시키고 함수가 종료될때까지 기다리면 누수는 없으나, 페이지 이동을 하게되면 타이머는 여전히 메모리를 차지하고 있게 됨.

 


App.jsx

import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";

export default function Home() {
  // const [state, setState] = useState(false);
  const navigate = useNavigate();
  let timerId = null;

  // Leading Edge Throttling
  const throttle = (delay) => {
    if (timerId) {
      // timerId가 있으면 바로 함수 종료
      return;
    }
    // setState(!state);
    console.log(`API요청 실행! ${delay}ms 동안 추가요청 안받음`);
    timerId = setTimeout(() => {
      console.log(`${delay}ms 지남 추가요청 받음`);
      // alert("Home / 쓰로틀링 쪽 API호출!");
      timerId = null;
    }, delay);
  };

  // Trailing Edge Debouncing
  const debounce = (delay) => {
    if (timerId) {
      // 할당되어 있는 timerId에 해당하는 타이머 제거
      clearTimeout(timerId);
    }
    timerId = setTimeout(() => {
      // timerId에 새로운 타이머 할당
      console.log(`마지막 요청으로부터 ${delay}ms지났으므로 API요청 실행!`);
      timerId = null;
    }, delay);
  };

  useEffect(() => {
    return () => {
      // 페이지 이동 시 실행
      if (timerId) {
        // 메모리 누수 방지
        clearTimeout(timerId);
      }
    };
  }, [timerId]);

  return (
    <div style={{ paddingLeft: 20, paddingRight: 20 }}>
      <h1>Button 이벤트 예제</h1>
      <button onClick={() => throttle(2000)}>쓰로틀링 버튼</button>
      <button onClick={() => debounce(2000)}>디바운싱 버튼</button>
			<div>
        <button onClick={() => navigate("/company")}>페이지 이동</button>
      </div>
    </div>
  );
}

 

1. 쓰로틀링

쓰로틀링 버튼을 누르고 가만히 있으면 2초 지난후에 요청을 받음.

 

쓰로틀링 버튼을 누르고 페이지 이동을 하면 기존요청에서 아무것도 리턴하지 않음 (추가 요청을 받지 않음)

 

2. 디바운싱

디바운싱 버튼을 반복해서 누르면 그 사이에는 실행되지 않고, 마지막요청 기준으로 2초 이후에 실행됨.

 


lodash

 

lodash 를 사용하면, useCallback 이 있어야함.

useCallback 을 통해 마운트 시에, debounce 를 기억하게되면 이 클로저 함수는 외부 함수의 변수에 계속해서 참조하여 갖고있기 때문에 타이머 아이디를 기억할 수 있게 됨.

import "./App.css";
import { useState, useCallback } from "react";
import _ from "lodash";

function App() {
  const [searchText, setSearchText] = useState("");
  const [inputText, setInputText] = useState("");

  const handleSearchText = useCallback(
    _.debounce((text) => setSearchText(text), 2000),
    []
  );

  const handleChange = (e) => {
    setInputText(e.target.value);
    handleSearchText(e.target.value);
  };

  return (
    <div
      style={{
        paddingLeft: 20,
        paddingRight: 20,
      }}
    >
      <h1>디바운싱 예제</h1>
      <br />
      <input
        placeholder="입력값을 넣고 디바운싱 테스트를 해보세요."
        style={{ width: "300px" }}
        onChange={handleChange}
        type="text"
      />
      <p>Search Text: {searchText}</p>
      <p>Input Text: {inputText}</p>
    </div>
  );
}

export default App;

'React' 카테고리의 다른 글

[React] 인증/ 인가 (세션)  (0) 2023.07.05
[React] 인증/ 인가 (쿠키)  (0) 2023.07.05
[React] React Query  (0) 2023.07.05
[React] custom hooks  (0) 2023.07.05
[React] Thunk  (0) 2023.07.04