用户停止输入时在React中搜索

shi*_*ite 32 settimeout reactjs

我需要在用户停止输入时执行搜索.我知道我应该使用setTimeout().但是使用Reactjs我无法找到它是如何工作的.有人可以告诉我如何在用户停止键入几秒钟时调用一个方法(将处理搜索)(假设5).我无法弄清楚在哪里编写代码以检查用户是否已停止输入.

import React, {Component, PropTypes} from 'react';

export default class SearchBox extends Component {

    state={
      name:" ",
    }

    changeName = (event) => {
        this.setState({name: event.target.value}); 
    }

    sendToParent = () => {
        this.props.searching(this.state.name);
    }

    render() {
        return (
            <div>
                 <input type="text"  placeholder='Enter name you wish to Search.'  onChange={this.changeName} />

            </div>
        );
    }
}   
Run Code Online (Sandbox Code Playgroud)

我想在用户停止输入时调用sendToParent方法.

ano*_*ewb 69

使用 useEffect 钩子实现:

function Search() {
  const [searchTerm, setSearchTerm] = useState('')

  useEffect(() => {
    const delayDebounceFn = setTimeout(() => {
      console.log(searchTerm)
      // Send Axios request here
    }, 3000)

    return () => clearTimeout(delayDebounceFn)
  }, [searchTerm])

  return (
    <input
      autoFocus
      type='text'
      autoComplete='off'
      className='live-search-field'
      placeholder='Search here...'
      onChange={(e) => setSearchTerm(e.target.value)}
    />
  )
}
Run Code Online (Sandbox Code Playgroud)

  • 简单又具有魅力。该解决方案需要更多票数 (11认同)
  • 制作了一个codesandbox,以防有任何帮助 https://codesandbox.io/s/search-input-settimeout-wszrv?file=/src/Search.tsx:205-417 (6认同)
  • @IndikaK 它会重新渲染,因为 &lt;input&gt; 元素上有一个 onChange ,每次用户按下某个键时都会调用 setState 。 (4认同)
  • 小心,这将重新渲染组件 (3认同)
  • 可惜@AljohnYamaro 被忽略了。他在那里得到了很好的观点 (2认同)
  • @AljohnYamaro,你能解释一下为什么它会被重新渲染吗? (2认同)

Sab*_*san 49

您可以setTimeout按照以下方式使用代码,

state = {
    name: '',
    typing: false,
    typingTimeout: 0
}
changeName = (event) => {
    const self = this;

    if (self.state.typingTimeout) {
       clearTimeout(self.state.typingTimeout);
    }

    self.setState({
       name: event.target.value,
       typing: false,
       typingTimeout: setTimeout(function () {
           self.sendToParent(self.state.name);
         }, 5000)
    });
}
Run Code Online (Sandbox Code Playgroud)

此外,您需要changeName在构造函数中绑定处理函数.

constructor(props) {
   super(props);
   this.changeName = this.changeName.bind(this);
}
Run Code Online (Sandbox Code Playgroud)

  • 我迟到了,但我想指出在使用es6箭头语法时你不需要绑定changeName函数.https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions (11认同)
  • 出于同样的原因,你不需要设置`const self = this`.`this`已经绑定到类范围. (5认同)
  • 在这里必须输入** typing **字段吗? (3认同)

sta*_*ool 27

这个(use-debounce)很好而且简单。

设置

yarn add use-debounce
Run Code Online (Sandbox Code Playgroud)

或者

npm i use-debounce --save
Run Code Online (Sandbox Code Playgroud)

文档中的使用示例

import React, { useState } from 'react';
import { useDebounce } from 'use-debounce';

export default function Input() {
  const [text, setText] = useState('Hello');
  const [value] = useDebounce(text, 1000);

  return (
    <div>
      <input
        defaultValue={'Hello'}
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
      <p>Actual value: {text}</p>
      <p>Debounce value: {value}</p>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

我现在喜欢的东西,将来可能会有所不同!:

  • 易于设置和使用
  • 更少的样板代码
  • 适度的评级 (~1K) 和使用量 (npm - 200K 下载量/周)
  • 支持超时、MaxWait等功能

  • 这应该是公认的答案!这个答案节省了我很多时间(和代码) (3认同)

小智 21

另一种与我合作的方式:

class Search extends Component {
  constructor(props){
    super(props);
    this.timeout =  0;
  }

  doSearch(evt){
    var searchText = evt.target.value; // this is the search text
    if(this.timeout) clearTimeout(this.timeout);
    this.timeout = setTimeout(() => {
      //search function
    }, 300);
  }

   render() {
    return (
      <div className="form-group has-feedback">
        <label className="control-label">Any text</label>
        <input ref="searchInput" type="text" onChange={evt => this.doSearch(evt)} />
      </div>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)


Hir*_*hut 17

我已经使用了这个自定义挂钩,它工作完美,仍然没有问题。

export function useSearchDebounce(delay = 350) {
  const [search, setSearch] = useState(null);
  const [searchQuery, setSearchQuery] = useState(null);

  useEffect(() => {
    const delayFn = setTimeout(() => setSearch(searchQuery), delay);
    return () => clearTimeout(delayFn);
  }, [searchQuery, delay]);

  return [search, setSearchQuery];
}
Run Code Online (Sandbox Code Playgroud)

在任何地方使用,例如

const [search, setSearch] = useSearchDebounce();

<input onChange={(e) => setSearch(e.target.value)}/>
Run Code Online (Sandbox Code Playgroud)

  • 这是最简单、最干净的解决方案,而且它足够通用,可以用于任何值,而不仅仅是搜索字段。 (2认同)

Aki*_*nka 13

我使用了 lodash 的 debounce 功能

onChangeSearchInput = (evt)=> {
    this.debouncedSearch(evt.target.value);
};

debouncedSearch = debounce(function (query) {
    this.setState({query});
}, 1000);
Run Code Online (Sandbox Code Playgroud)

在我的渲染方法的某个地方,我有这个输入字段

<input
    type='text'
    onChange={this.onChangeSearchInput}
    className='uk-input'
    placeholder={'search by name or email...'}
   />
Run Code Online (Sandbox Code Playgroud)

  • 仅仅为了延迟一个函数而调用一个全新的(巨大的)库是不好的,对吧? (3认同)
  • @w3debugger 你是对的。但我的项目中已经有 lodash 用于其他用途。 (2认同)
  • 您还可以只导入特定函数,而不是引入整个库。 (2认同)

Rah*_*hul 10

我认为我们可以以更简单、更简洁的方式来完成,而无需像这样中断调用完整组件生命周期的状态参数:

constructor(props) {
    super(props);

    //Timer
    this.typingTimeout = null;

    //Event
    this.onFieldChange = this.onFieldChange.bind(this);

    //State
    this.state = { searchValue: '' }; 
}   


 /**
 * Called on the change of the textbox.
 * @param  {[Object]} event [Event object.]
 */
onFieldChange(event) {
    // Clears the previously set timer.
    clearTimeout(this.typingTimeout);

    // Reset the timer, to make the http call after 475MS (this.callSearch is a method which will call the search API. Don't forget to bind it in constructor.)
    this.typingTimeout = setTimeout(this.callSearch, 475);

    // Setting value of the search box to a state.
    this.setState({ [event.target.name]: event.target.value });
}


<div className="block-header">
     <input
           type="text"
           name="searchValue"
           value={this.state.searchValue}
           placeholder="User Name or Email"
           onChange={this.onFieldChange}
     />
</div>
Run Code Online (Sandbox Code Playgroud)


小智 7

您可以将 React hooks useEffect与 setTimeOut 函数一起使用,因为它总是返回计时器 id,并且您可以轻松地使用该 id 清除计时器,如下所示

export const Search = () => {
const [term, setTerm] = useState();
const [results, setResult] = useState([]);

useEffect(() => {
    const searchWiki = async () => {
        const { data } = await axios.get('https://en.wikipedia.org/w/api.php', {
            params: {
                srsearch: term,
            },
        });

        setResult(data.query.search);
    };
    const timerId = setTimeout(() => {
        searchWiki();
     // make a request after 1 second since there's no typing 
    }, 1000);

    return () => {
        clearTimeout(timerId);
    };
}, [term]);
Run Code Online (Sandbox Code Playgroud)


Hei*_*erg 5

自定义钩子怎么样?

import {useEffect, useRef, useState} from "react";

export default function useSearchInputState(searchHandler) {
  
  // to prevent calling the handler on component mount
  const didMountRef = useRef(false);

  const [searchValue, setSearchValue] = useState(null);

  useEffect(() => {
    let delayDebounceFn;

    if (didMountRef.current) {
      delayDebounceFn = setTimeout(searchHandler, 600)
    } else {
      didMountRef.current = true;
    }

    return () => clearTimeout(delayDebounceFn);
  }, [searchValue]); // eslint-disable-line react-hooks/exhaustive-deps

  return [searchValue, setSearchValue];

}
Run Code Online (Sandbox Code Playgroud)

用法:

function MyComponent(props) {

  const [searchValue, setSearchValue] = useSearchInputState(() => {
    resetData(searchValue ?? null, selectedFilterPos); // replace with your code
  });

  return (
    <input className="Search"
           onChange={e => setSearchValue(e?.target?.value ?? null)}
      />
  );
}
Run Code Online (Sandbox Code Playgroud)