键入时ReactJS延迟onChange

时间:2018-10-30 19:42:37

标签: javascript reactjs

我需要更改状态以维护用户键入的字符串。但是我想延迟一个操作,直到用户停止键入为止。但是我不能完全专注于如何做到这两者。

因此,当用户停止输入时,我希望触发一个动作,而不是之前。有什么建议吗?

8 个答案:

答案 0 :(得分:16)

带有React挂钩和功能组件

要保留用户输入的字符串,请使用useState钩子来存储用户输入的文本。然后将该状态赋予输入的值。另外,请确保在输入的setState事件处理程序上使用onChange,否则输入值不会更改。

要仅在用户停止键入后的某个时间触发操作,可以将useEffect钩子与setTimeout一起使用。在这种情况下,我们想在输入值更改时触发useEffect,因此我们将创建一个useEffect钩子,并在其依赖项数组上为其提供具有输入值的变量。给useEffect的功能应使用setTimeout在所需的延迟时间后触发动作。同样,赋予useEffect的函数应返回清除超时设置的清除函数。这样可以避免对不再与用户相关的输入值执行操作。

下面是一个应用程序的小示例,该应用程序使用上述步骤来使用户键入的字符串保持可见,并在用户停止键入500ms后显示完成的字符串。

function App() {
  const [query, setQuery] = useState("");
  const [displayMessage, setDisplayMessage] = useState("");

  useEffect(() => {
    const timeOutId = setTimeout(() => setDisplayMessage(query), 500);
    return () => clearTimeout(timeOutId);
  }, [query]);

  return (
    <>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <p>{displayMessage}</p>
    </>
  );
}

答案 1 :(得分:6)

使用 React Hooks 和 Function 组件

const [timer, setTimer] = useState(null);

function changeDelay(change) {
    if (timer) {
      clearTimeout(timer);
      setTimer(null);
    }
    setTimer(
      setTimeout(() => {
        console.log(change);
      }, 3000)
    );
}

输入法

<input type="text" onChange={(e) => { changeDelay(e.target.value); }} />

答案 2 :(得分:1)

一种方法是让您的onChange处理程序执行两个函数:

  • 立即更新状态的功能
  • 去抖功能

示例代码:

import debounce from 'lodash.debounce';

class Foo extends React.Component {
  constructor() {
    super()

    this.state = {
      value: ''
    }

    // Delay action 2 seconds
    this.onChangeDebounced = debounce(this.onChangeDebounced, 2000)
  }

  handleInputChange = (e: Event) => {
    // Immediately update the state
    this.setState({
      value: e.target.value
    })

    // Execute the debounced onChange method
    this.onChangeDebounced(e)
  }

  onChangeDebounced = (e: Event) => {
    // Delayed logic goes here
  }

  render() {
    return (
      <input onChange={this.handleInputChange} value={this.state.value} />
    )
  }
}

答案 3 :(得分:1)

用户输入文字后,您将需要使用client.shard.broadcastEval('this.users.size').then(i => console.log(i)) 来启动计时器。如果用户输入其他字符,请重新启动计时器。如果用户没有在计时器完成之前再次键入,它将触发切换复选框的操作:

setTimeout
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      text: '',
      checked: false
    };
    this.timer = null;
  }
  
  componentDidUpdate (prevProps, prevState) {
    if(prevState.text !== this.state.text) {
      this.handleCheck();
    }
  }
  
  onChange = e => {
    this.setState({
      text: e.target.value
    });
  };
  
  handleCheck = () => {
    // Clears running timer and starts a new one each time the user types
    clearTimeout(this.timer);
    this.timer = setTimeout(() => {
      this.toggleCheck();
    }, 1000);
  }
  
  toggleCheck = () => {
    this.setState( prevState => ({ checked: !prevState.checked }));
  }
  
  render () {
    return (
      <div>
        <input value={this.state.text} onChange={this.onChange} placeholder="Start typing..." /><br/>
        <label>
          <input type="checkbox" checked={this.state.checked} onChange={this.toggleCheck} />
          Toggle checkbox after user stops typing for 1 second
        </label>
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

答案 4 :(得分:0)

您可以在onChange事件上进行反跳(如果用户输入onchange事件将不会执行)

警告-请记住,在渲染上创建函数是一种不好的做法。 我这样做是为了说明解决方案。 一种更安全的解决方案是使用Component类,该类在其构造函数上创建去抖动处理程序。

class DebouncedInput extends React.Component {
  constructor() {
    super();

    // Creating the debouncedOnChange to avoid performance issues

    this._debouncedOnChange = _.debounce(
      this.props.onChange, 
      this.props.delay
    );
  }

  render () {
    const { onChange, delay, ...rest } = this.props;
    return (
      <input onChange={this._debouncedOnChange} {..rest} />
    )
  }
}

以下示例

function DebouncedInput (props) {
  const { onChange, delay = 300, ...rest } = props;
 
  
  return (
    <input 
      {...rest}
      onChange={ _.debounce(onChange, delay)}
    />
  )
}

function App() {
  return (
    <div>
      <DebouncedInput 
        type="text"
        placeholder="enter"
        delay={2000}
        onChange={() => console.log('changing')}
      />
    </div>
  )
}

ReactDOM.render(
  <App/>,
  document.querySelector('#app')
);
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.2/umd/react.production.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.2/umd/react-dom.production.min.js"></script>
</head>
<body>
 <div id="app"></div>
</body>
</html>

答案 5 :(得分:0)

调用除第一次以外的每个状态更新:

实际上,我遇到了同样的问题,但是有一点setTimeout可以帮助我进行第一次安装支票ref

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

const Search = () => {
    const filterRef = useRef(); // use ref to call the API call all time except first time
    const [serpQuery, setSerpQuery] = useState('');

    useEffect(() => {
        let delayTimeOutFunction;

        if(!filterRef.current) {
            filterRef.current = true;

        } else { // componentDidMount equivalent
            delayTimeOutFunction = setTimeout(() => {
                console.log('call api: ', serpQuery)
            }, 700); // denounce delay
        }
        return () => clearTimeout(delayTimeOutFunction);
    }, [serpQuery]);

    return (
      <input value={serpQuery} onChange={e => setSerpQuery(e.target.value)} />
    );
};

答案 6 :(得分:0)

您可以专门为此目的构建自定义钩子,并像 useState 钩子一样使用它。这更像是 jnforja 的 answer

的扩展

import { useEffect, useState } from "react";
const useDebounce = (initialValue = "", delay) => {
  const [actualValue, setActualValue] = useState(initialValue);
  const [debounceValue, setDebounceValue] = useState(initialValue);
  useEffect(() => {
    const debounceId = setTimeout(() => setDebounceValue(actualValue), delay);
    return () => clearTimeout(debounceId);
  }, [actualValue, delay]);
  return [debounceValue, setActualValue];
};

export default useDebounce;

使用它就像 useState 钩子和延迟值

const [value, setValue] = useDebounce('',1000)

如果需要,您还可以查看 this 文章,解释实现。

答案 7 :(得分:0)

使用 React Hooks - useRef

const timer = useRef(null)
    
useEffect(() => {
    
    clearTimeout(timer.current)
    timeout.current = setTimeout(() => {
     // your logic
    },1000)
    
},[value])