💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、豆包、星火、月之暗面及文生图、文生视频 广告
~~~js import debounce from 'lodash/debounce'; export default class Search extends Component { constructor(props) { super(props) this.handleSearch = debounce(this.handleSearch, 500); } handleSubmit = (e) => { e.preventDefault(); this.handleSearch(); } handleSearch = () => { ... } render() { return ( <form onSubmit={this.handleSubmit}><form> ) } } ~~~ 这是一段在react类组件中使用节流函数的示例代码,而当你想在函数式组件中使用节流函数时,你可能会自然而然的想要这么做:: ~~~js import React, {useState} from "react"; import debounce from 'lodash/debounce'; const sendQuery = (query) => console.log(`Querying for ${query}`); const Search = () => { const [userQuery, setUserQuery] = useState(""); const delayedQuery = debounce(q => sendQuery(q), 500); const onChange = e => { setUserQuery(e.target.value); delayedQuery(e.target.value); }; return ( <div> <label>Search:</label> <input onChange={onChange} value={userQuery} /> </div> ); } ~~~ 但这样子做防抖函数并没有发挥作用,仅仅是把反应时间延后了500毫秒。这是因为函数时组件每次渲染结束之后,内部的变量都会被释放,重新渲染时所有的变量会被重新初始化,产生的结果就是每一次都注册和执行了setTimeout函数。想要得到正确的运行结果,必须以某种方式存储那些本会被删除的变量和方法的引用。很遗憾,没办法直接使用useState去存储,但是我们可以直接构造一个新的hook去解决这个问题。 ~~~js import React, { useState, useEffect } from 'react'; // Our hook export default function useDebounce(value, delay) { // State and setters for debounced value const [debouncedValue, setDebouncedValue] = useState(value); useEffect( () => { // Set debouncedValue to value (passed in) after the specified delay const handler = setTimeout(() => { setDebouncedValue(value); }, delay); // Return a cleanup function that will be called every time ... // ... useEffect is re-called. useEffect will only be re-called ... // ... if value changes (see the inputs array below). // This is how we prevent debouncedValue from changing if value is ... // ... changed within the delay period. Timeout gets cleared and restarted. // To put it in context, if the user is typing within our app's ... // ... search box, we don't want the debouncedValue to update until ... // ... they've stopped typing for more than 500ms. return () => { clearTimeout(handler); }; }, // Only re-call effect if value changes // You could also add the "delay" var to inputs array if you ... // ... need to be able to change that dynamically. [value] ); return debouncedValue; } ~~~ 构造完useDebounce函数后,我们可以这样使用: ~~~js import React, { useState, useEffect } from 'react'; import useDebounce from './use-debounce'; const sendQuery = (query) => console.log(`Querying for ${query}`); // Usage function Search() { // State and setter for search term const [searchTerm, setSearchTerm] = useState(''); // State and setter for search results const [results, setResults] = useState([]); // State for search status (whether there is a pending API request) const [isSearching, setIsSearching] = useState(false); // Now we call our hook, passing in the current searchTerm value. // The hook will only return the latest value (what we passed in) ... // ... if it's been more than 500ms since it was last called. // Otherwise, it will return the previous value of searchTerm. // The goal is to only have the API call fire when user stops typing ... // ... so that we aren't hitting our API rapidly. const debouncedSearchTerm = useDebounce(searchTerm, 500); // Here's where the API call happens // We use useEffect since this is an asynchronous action useEffect( () => { // Make sure we have a value (user has entered something in input) if (debouncedSearchTerm) { // Set isSearching state setIsSearching(true); // Fire off our API call sendQuery(debouncedSearchTerm); // Set back to false since request finished setIsSearching(false); // Set results state setResults(results); } else { setResults([]); } }, // This is the useEffect input array // Our useEffect function will only execute if this value changes ... // ... and thanks to our hook it will only change if the original ... // value (searchTerm) hasn't changed for more than 500ms. [debouncedSearchTerm] ); return ( <div> <label>Search:</label> <input onChange={e => setSearchTerm(e.target.value) /> </div> ); } ~~~ 上面的方法还是太复杂了,通过使用useRef和useCallback我们可以更简单的去解决这个问题: ~~~js const SearchFixed = () => { const [userQuery, setUserQuery] = useState(""); const delayedQuery = useRef(debounce(q => sendQuery(q), 500)).current; const onChange = e => { setUserQuery(e.target.value); delayedQuery(e.target.value); }; return ( <div> <label>Search Fixed:</label> <input onChange={onChange} value={userQuery} /> </div> ); }; ~~~ 或是: ~~~js const SearchFixed = () => { const [userQuery, setUserQuery] = useState(""); const delayedQuery = useCallback(debounce(q => sendQuery(q), 500), []); const onChange = e => { setUserQuery(e.target.value); delayedQuery(e.target.value); }; return ( <div> <label>Search Fixed:</label> <input onChange={onChange} value={userQuery} /> </div> ); }; ~~~