Debounce

์ฐจ์ฐจยท2023๋…„ 1์›” 28์ผ
post-thumbnail

๐Ÿ”ฅ Debounce

  • debounce๋Š” dom ์Šคํฌ๋กค ๋˜๋Š” ์ˆซ์ž ์ž…๋ ฅ์— ๋”ฐ๋ฅธ api๊ฐ’ ํ˜ธ์ถœ ๊ฐ™์ด ์ด๋ฒคํŠธ๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ๋งŽ์€ ํ˜ธ์ถœํ•  ํ•˜๋Š” ๊ฒฝ์šฐ ์ง€์ •ํ•œ ์‹œ๊ฐ„ ๋™์•ˆ ํ˜ธ์ถœ๋„ค ์ œ์•ฝ์„ ๊ฑธ์–ด api ํ˜ธ์ถœ ๊ณผ๋ถ€ํ•˜๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๊ธฐ์ˆ ์ด๋‹ค.
  • debounce๋Š” ํ˜ธ์ถœ์‹œ ์ง€์ •๋œ ์‹œ๊ฐ„ ์ดํ›„ ํ˜ธ์ถœ๋œ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ ์ด๋ฒคํŠธ๋งŒ ์‹คํ–‰๋˜๋Š” ๊ธฐ์ˆ ์ด๋‹ค. (์ง€์ •๋œ ์‹œ๊ฐ„์ด 0.5์ดˆ์ด๊ณ , 0.5์ดˆ๋‚ด์— 5๋ฒˆ์ด ํ˜ธ์ถœ ๋˜์—ˆ๋‹ค๋ฉด ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ํ˜ธ์ถœ๋œ 5๋ฒˆ์งธ api๋งŒ ์„œ๋ฒ„๋กœ ํ˜ธ์ถœํ•œ๋‹ค๋Š” ์˜๋ฏธ)


๋ฌธ์ œ
input์— ๊ฐ’์ด ์ž…๋ ฅ๋  ๋•Œ๋งˆ๋‹ค ๊ฒ€์ƒ‰๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•œ๋‹ค. ํ•˜์ง€๋งŒ onChange๊ฐ€ ์ด๋ฃจ์–ด์งˆ ๋•Œ๋งˆ๋‹ค ์„œ๋ฒ„๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด 2๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

  1. race condition์— ๋”ฐ๋ผ ์‘๋‹ต ์ˆœ์„œ๊ฐ€ ์—‰์ผœ ์›ํ•˜์ง€ ์•Š๋Š” ๊ฒ€์ƒ‰๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.
  2. ์„œ๋ฒ„์— ๊ณผ๋„ํ•œ ์š”์ฒญ์„ ๋ณด๋‚ด ๋ถ€ํ•˜๋ฅผ ์ผ์œผํ‚ค๋Š” ์›์ธ์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Debouce๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•œ๋‹ค.

lodash๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Debouce ๊ตฌํ˜„ํ•˜๊ธฐ

import React, { useCallback, useMemo, useState } from "react";
import { debounce } from "lodash";
import { ICharacter } from "./types";

async function searchCharacter(keyword: string): Promise<ICharacter[]> {
  const response = await fetch(
    `https://swapi.dev/api/people?search=${keyword}`
  ).then((res) => res.json());
  return response.results;
}

function App() {
  const [keyword, setKeyword] = useState("");
  const [data, setData] = useState<ICharacter[]>([]);
  
  // ์ฒซ๋ฒˆ์งธ ์ธ์ž๋กœ๋Š” ์ฝœ๋ฐฑํ•จ์ˆ˜, ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ๋Š” ์‹œ๊ฐ„(๋ฐ€๋ฆฌ์„ธ์ปจ๋“œ)๋ฅผ ์ „๋‹ฌํ•ด์ค€๋‹ค.
  // searchCharacter๊ฐ€ 1์ดˆ ๋™์•ˆ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ทธ ๋•Œ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค.
  const handleSearch = useCallback(
    debounce((keyword: string) => {
      searchCharacter(keyword).then((res) => setData(res));
    }, 1000),
    []
  );

  const handleChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      e.preventDefault();
      setKeyword(e.target.value);
      handleSearch(e.target.value);
    },
    [handleSearch]
  );

  return (
    <div className="App">
      <form>
        <input type="text" onChange={handleChange} value={keyword} />
      </form>
      <ul>
        {data?.map((item, i) => (
          <li key={i}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

useCallback์— ESLint ๊ฒฝ๊ณ  ๋ฐœ์ƒ

React Hook useCallback received a function whose dependencies are unknown.
 Pass an inline function instead.eslintreact-hooks/exhaustive-deps

โœ…ย ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

useCallback โ†’ useMemo๋กœ ๋ณ€๊ฒฝ

const handleSearch = useMemo(
    () =>
      debounce((keyword: string) => {
        searchCharacter(keyword).then((res) => setData(res));
      }, 1000),
    []
  );
profile
๋‚˜๋Š”์•ผ ํ”„๋ฆฐ์ด

0๊ฐœ์˜ ๋Œ“๊ธ€