혼자 적어보는 노트

[React] 리액트에서 디바운싱 구현하기 본문

React

[React] 리액트에서 디바운싱 구현하기

jinist 2022. 3. 14. 17:07

 

이전에 Vanilla Javascript로 디바운싱을 구현해본 적 있었는데

이전에 만들고 있던 React 사이드 프로젝트인 todoList를 리팩토링 하면서 적용해보면 좋을 것 같아서

디바운싱을 간단하게 구현해보려고 한다.

 

debounce

연속으로 호출되는 함수들 중 마지막 함수(or 처음 호출된 함수)만 호출하도록 하는 것

 


 

todoList의 step값이 onChange될 경우

변경한 값이 바로 데이터베이스에 저장되기를 원하는 상황이다.

현재 데이터베이스는 firebase의 realtime base를 사용하고 있었고

value가 변경될 때마다 데이터통신을 하는 것이 부담이 된다고 생각이 들었다.

 

[코드의 일부]

import React, { useCallback, useState } from "react";
import CheckBtn from "./Button/CheckBtn";
import CloseBtn from "./Button/CloseBtn";
import { deleteStep } from "../modules/todo";
import { onRemoveStep, onUpdateStep, onCheckStep } from "../lib/firebase/todosData";

const StepInput = ({ step, index, todoId, type }) => {
  const [inputValue, setInputValue] = useState(step.title);
  const [timer, setTimer] = useState(0);

  const onChange = (event, index) => {
    setInputValue(event.target.value);

    if (timer) {
      clearTimeout(timer);
    }
    const newTimer = setTimeout(() => {
      onUpdateStep(todoId, event.target.value, index, type);
    }, 1000);
    setTimer(newTimer);
  };

  return (
    <div className="input-wrap">
      <input
        className={step.check ? "check" : ""}
        value={inputValue}
        onChange={(event) => {
          onChange(event, index);
        }}
      />
    </div>
  );
};

export default StepInput;

 

value의 값은 useState로 ui상에서 즉각적으로 변경이 되도록 하고

timer를 생성하여 timer가 있으면 clearTimeout을 해주고 새로운 timer를 만들어서

Timer에 새로운 Timer를 담아주는 방식이다.

 

실제 비동기 통신을 해야할 땐 async await으로 활용이 가능하다.

  const [inputValue, setInputValue] = useState(step.title);
  const [timer, setTimer] = useState(0);

  const onChange = (event, index) => {
    setInputValue(event.target.value);

    if (timer) {
      clearTimeout(timer);
    }
    const newTimer = setTimeout(async() => {
      try {
        /// api요청
      }
      catch(error){
      	console.log(error)
      }
    }, 1000);
    setTimer(newTimer);
  };

 

구현해보고 나니 다른 컴포넌트에도 debounce를 해야해서

costom hook으로 만들어보았다.

 

useDebounce.js

export const useDebounce = () => {
  const [timer, setTimer] = useState(0);
  const debounce = (callback, data) => {
    if (timer) {
      clearTimeout(timer);
    }
    const newTimer = setTimeout(async () => {
      callback(...data);
    }, 800);
    setTimer(newTimer);
  };
  return debounce;
};

callback함수와 해당 콜백함수의 파라미터데이터를 인자로 받고

해당 함수를 실행하도록 하였다.

 

[적용한 컴포넌트의 코드]

  const [inputValue, setInputValue] = useState(step.title);
  const debounce = useDebounce();

  const onChange = (event) => {
    setInputValue(event.target.value);
    debounce(onUpdateStep, [todoId, event.target.value, index, type]);
  };

 

Comments