【问题标题】:Trying to implement debounce in React Project尝试在 React 项目中实现去抖动
【发布时间】:2021-10-28 20:58:37
【问题描述】:

我正在尝试在一个小型/测试 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 秒等待以清楚地查看它是否正常工作,但是每次我更改输入文本时,它都会毫无延迟地调用该函数。有谁知道为什么会这样?

谢谢

【问题讨论】:

  • 好像你在调用handleTextChange 时立即调用了newHandleTextChange。也许你应该让它成为一个匿名函数? const newHandleTextChange = ((val) =&gt; debounce(() =&gt; handleTextChange(val), 5000))
  • 我很好奇你在这里想要什么效果;我经常看到使用useEffect hooks 实现去抖动
  • @Nick 在显示选项列表之前等待的想法,例如 500 毫秒。比如我想等待500ms,因为这通常是用户输入单词的时间,所以过滤器会根据输入的单词进行过滤

标签: reactjs debouncing


【解决方案1】:

在 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;

【讨论】:

  • 我认为您只是添加了一个额外的状态变量并有一个超时来更新其状态,而不是使用像这里这样的去抖动方法:dmitripavlutin.com/react-throttle-debounce 但是,我不是在寻找 Lodash 解决方案
  • 嗯,如果没有另一个状态,您可能不会运气好去抖动受控输入。
  • 知道了。谢谢
【解决方案2】:
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>
);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-17
    • 2017-07-01
    • 1970-01-01
    • 2022-06-16
    • 2019-12-31
    • 2020-11-01
    • 2017-06-16
    相关资源
    最近更新 更多