React JS setTimeout 被多次执行

React JS setTimeout gets executed multiple times

大家好,我是 React JS 的新手,我遇到了一个无法找出原因的问题。因此,我正在尝试制作一个搜索字段组件,该组件仅在用户完成输入后触发 API 调用。发生的事情是,在输入 setTimeout 后,搜索被多次执行。恐怕这会造成多次不必要的 api 调用。非常感谢您的帮助。谢谢!

main.tsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <App />
)

App.tsx

import { useState } from 'react'
import fetchData from './utils/fetch'
import { WeatherModel } from './model/Weather'

function SearchField() {
  let timer = 0
  const [city, setCity] = useState('New York')

  const handleFetchWeather = async () => {
    const data: WeatherModel = await fetchData(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${WEATHER_API_KEY}&units=metric`)
    console.log(data)
  }

  const handleSetCity = (e) => {
    if(timer) clearTimeout(timer)
    setCity(e.target.value)
    timer = setTimeout(() => {
      console.log(city)
      // handleFetchWeather()
    }, 2000)
  }

  return (
    <div className="search">
      <input type="text" placeholder="Search city..." onChange={handleSetCity} />
    </div>
  )
}

export default SearchField

键入搜索后的多个控制台日志

您可以使用 Debounce:

这是一个例子:

useEffect(() => {
    let debounce;
    
    if (debounce) {
        clearTimeout(debounce);
        debounce = null;
    }

    debounce = setTimeout(() => {
        // DO SOMETHING AFTER TIMEOUT ENDS
        setSearchEntry(searchValue);
    }, 1 * 1000);
    
}, [setSearchEntry, searchValue]);

祝你好运:)

输入字段中的 onChange 事件将在每次输入新字符时触发,了解更多信息 here

在这种情况下,我认为最好的解决方案是添加触发 handleSetCity() 的提交按钮,并且您的 onChange 应该设置一个状态变量,以便更容易处理数据。

我同意 debounce 解决方案,但如果你不知道,这段代码对你来说可能更简单,所以每次组件重新渲染时,计时器都会再次设置为 0,所以为了要持久化该值,假设您需要将其置于一种状态,就像这样:

import { useState } from "react";

function SearchField() {
  const [city, setCity] = useState("New York");
  const [timer, setTimer] = useState(0);

  const handleFetchWeather = async () => {
    const data = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${WEATHER_API_KEY}&units=metric`
    );
    console.log(data);
  };

  const handleSetCity = (e) => {
    if (timer) {
      clearTimeout(timer);
    }
    setCity(e.target.value);
    setTimer(
      setTimeout(() => {
        console.log(city);
        // handleFetchWeather()
      }, 2000)
    );
  };

  return (
    <div className="search">
      <input
        type="text"
        placeholder="Search city..."
        onChange={handleSetCity}
      />
    </div>
  );
}

export default SearchField;

现在,每次重新渲染,组件都会记住它之前的值;)