尝试在 React 项目中实现去抖动

Trying to implement debounce in React Project

我正在尝试在 small/test React 应用程序中实现去抖动。

它只是一个从 API 获取数据的应用程序,它有一个自动完成的文本字段。

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

const API = 'https://jsonplaceholder.typicode.com/posts';

const AutoComplete2 = () => {

    const [ text, setText ] = useState("")
    const [ posts, setPosts ] = useState([])

    useEffect(() => {  
        async function fetchData() {
            const data = await axios.get(API);
            if(parseInt(data.status) !== 200)   return;

            setPosts(data.data)
        }
        fetchData();
    }, [])

    const handleTextChange = (event) => setText(event.target.value);

    const handleSelectOption = (str) => setText(str);

    const showOptions = useMemo(() => {
        if(text === '') return;

        const showPosts = [...posts].filter((ele) => ele.title.toLowerCase().includes(text.toLowerCase()));
        if(showPosts.length === 1) {
            setText(showPosts[0].title);
        } else {
            return (
                <div>
                    {showPosts.map((obj, index) => {
                        return (
                            <div key={index} >
                                <span onClick={() => handleSelectOption(obj.title)} style={{cursor: 'pointer'}}>
                                    {obj.title}
                                </span>
                            </div>
                        )
                    })}
                </div>
            )
        }
    }, [text, posts])

    // addding debounce
    const debounce = (fn, delay) => {
        let timer;
        return function() {
            let context = this;
            let args = arguments;
            clearTimeout(timer);
            timer = setTimeout(() => {
                fn.apply(context, args)
            }, delay);
        }
    }

    const newHandleTextChange = ((val) => debounce(handleTextChange(val), 5000));

    return (
        <div>
            <input type="text" value={text} onChange={newHandleTextChange} />
            {showOptions}
        </div>
    )
}

export default AutoComplete2;

应用程序有效,但去抖动无效。我添加了 5 秒的等待时间以清楚地查看它是否正常工作,但每次我更改输入文本时,它都会立即调用该函数。有谁知道为什么会这样?

谢谢

在 React 中去抖动的一种更惯用的方法是使用 useEffect 钩子并将去抖动的文本存储为不同的状态变量。然后,您可以 运行 根据该变量进行过滤。

import React, { useEffect, useState, useMemo } from "react";
import axios from "axios";

const API = "https://jsonplaceholder.typicode.com/posts";

const AutoComplete2 = () => {
  const [text, setText] = useState("");
  const [debouncedText, setDebouncedText] = useState("");
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    async function fetchData() {
      const data = await axios.get(API);
      if (parseInt(data.status) !== 200) return;

      setPosts(data.data);
    }
    fetchData();
  }, []);

  // This will do the debouncing
  // "text" will always be current
  // "debouncedText" will be debounced
  useEffect(() => {
    const timeout = setTimeout(() => {
      setDebouncedText(text);
    }, 5000);
    // Cleanup function clears timeout
    return () => {
      clearTimeout(timeout);
    };
  }, [text]);

  const handleTextChange = (event) => setText(event.target.value);

  const handleSelectOption = (str) => setText(str);

  const showOptions = useMemo(() => {
    if (debouncedText === "") return;

    const showPosts = [...posts].filter((ele) =>
      ele.title.toLowerCase().includes(debouncedText.toLowerCase())
    );
    if (showPosts.length === 1) {
      setText(showPosts[0].title);
    } else {
      return (
        <div>
          {showPosts.map((obj, index) => {
            return (
              <div key={index}>
                <span
                  onClick={() => handleSelectOption(obj.title)}
                  style={{ cursor: "pointer" }}
                >
                  {obj.title}
                </span>
              </div>
            );
          })}
        </div>
      );
    }
  }, [debouncedText, posts]);

  return (
    <div>
      <input type="text" value={text} onChange={handleTextChange} />
      {showOptions}
    </div>
  );
};

export default AutoComplete2;
import { useEffect, useState, useRef } from "react";
import axios from "axios";
import { backend_base_url } from "../constants/external_api";

export default function DebounceControlledInput() {
const [search_category_text, setSearchCategoryText] = useState("");
let debounceSearch = useRef();

useEffect(() => {
    const debounce = function (fn, interval) {
        let timer;
        return function (search_key) {
            clearTimeout(timer);
            timer = setTimeout(() => {
                fn(search_key);
            }, interval);
        };
    };
    const getCategories = function (search_key) {
        axios
            .get(`${backend_base_url}categories/${search_key}`)
            .then((response) => {
                console.log("API Success");
            })
            .catch((error) => {});
    };
    debounceSearch.current = debounce(getCategories, 300);
    //use for initial load
    //debounceSearch.current('');
}, []);

const searchCategory = (search_key) => {
    debounceSearch.current(search_key);
};

return (
    <form
        className="form-inline col-4"
        onSubmit={(e) => {
            e.preventDefault();
        }}
        autoComplete="off"
    >
        <input
            type="text"
            placeholder=""
            id="search"
            value={search_category_text}
            onChange={(e) => {
                searchCategory(e.target.value);
                setSearchCategoryText(e.target.value);
                e.preventDefault();
            }}
        />
    </form>
);
}