【问题标题】:How to make React input onChange set state only after onChange stops firing for set time?仅在 onChange 停止触发设定时间后,如何使 React 输入 onChange 设置状态?
【发布时间】:2020-03-28 21:39:31
【问题描述】:

简介 我在 create-react-app 应用程序中有一个用户输入元素。它允许用户键入一个数字,或使用箭头向上或向下增加数字。然后输出显示这个数字。

问题 用户想要到达某个数字,无论他们如何输入数字,都会有虚假的中间值触发 onChange 事件监听器。 如果用户尝试用键盘输入“15”,输出首先是“1”,然后是“15”,但我希望它只是“15”。我不想使用按钮/提交/等或其他事件。 如果用户按住从 0 到 500 的向上箭头,我想在“快速变化”输入减慢/停止后直接更新到 500。

目标 我想编写一个 onChange 函数,以便仅当 onChange not 自上次事件以来触发事件至少 600 毫秒时才更新输出。

详情 如果用户键入“1”然后键入“5”(表示“15”)的间隔小于 600 毫秒,则输出应更新为“15”,并跳过“1”。但是,如果用户在输入“1”和“5”之间花费的时间超过 600 毫秒,那么输出应该首先是“1”,然后是“5”。同样,如果用户上下快速按下增量箭头,则输出不应该更新,直到它们停止或减速。

在终端中,使用 create-react-app 制作一个 react 应用

create-react-app delay-output-app

代码 将其复制并粘贴到 delay-output-app/src/App.js 中,替换之前的所有代码。

import React, { useState, useEffect, useRef } from "react";
import "./App.css";

function App() {
  const inputRef = useRef();
  const [userValue, setUserValue] = useState(0);
  const [output, setOutput] = useState(0);

  const updateOutput = () => {
    setUserValue(inputRef.current.value);
    setOutput(inputRef.current.value);
    //if input is changing quickly - do not update output
    //only update output if input has not changed for 700ms since last change.
  };

  return (
    <div className="App">
      <br></br>
      <div>
        <h1>Input</h1>
        <input
          ref={inputRef}
          value={userValue}
          type="number"
          onChange={updateOutput}
        ></input>
      </div>
      <br></br>
      <div>
        <h1>Output</h1>
        {output}
      </div>
    </div>
  );
}

export default App;

在终端中运行应用程序...

cd delay-output-app
npm start

我尝试过使用 setTimeout 和 setInterval,但我一直遇到问题,尤其是与 react useEffect 挂钩时。同样,我不想使用按钮或让用户除了输入数字或单击箭头来更新输出之外需要按下任何东西。只有输入值之间的时间决定了输入值何时是中间值或应该更新输出。

更新以包括去抖 - 我的印象是去抖不应该在输入事件结束之前运行,但我仍然在等待期之后获得更新输出,不管如果用户输入事件结束。

带有 lodash debounce 的代码

import React, { useState, useRef } from "react";
import "./App.css";
import _ from "lodash";

function App() {
  const inputRef = useRef();
  const [userValue, setUserValue] = useState(0);
  const [output, setOutput] = useState(0);

  const updateOutput = _.debounce(
    () => setOutput(inputRef.current.value),
    700
  );
  //if input is changing quickly - do not update output
  //only update output if input has not changed for 700ms since last change.

  const updateUserValue = () => {
    setUserValue(inputRef.current.value);
  };
  return (
    <div className="App">
      <br></br>
      <div>
        <h1>Input</h1>
        <input
          ref={inputRef}
          value={userValue}
          type="number"
          onChange={() => {
            updateOutput();
            updateUserValue();
          }}
        ></input>
      </div>
      <br></br>
      <div>
        <h1>Output</h1>
        {output}
      </div>
    </div>
  );
}

export default App;


【问题讨论】:

  • 如果我理解正确,我想你会想要去抖动:lodash.com/docs/4.17.15#debounce 你的函数。这将延迟调用回调函数,直到经过设定的时间(700 毫秒)。
  • @MattCarlotta 我用过 lodash,但从不反跳 - 我会调查一下,谢谢。
  • 很确定 lodash 的去抖动功能会如你所愿。试一试,如果您仍然需要帮助,我将创建一个有效的代码框示例。
  • @MattCarlotta 是的,去抖成功了!花了这么多时间试图让自己去抖动!您想回答这个问题以便我接受吗?如果没有,我可以提出答案。
  • 继续为可能遇到相同问题的人提供解决方案。

标签: reactjs input settimeout setinterval onchange


【解决方案1】:

你可以试试这个方法

const updateOutput = () => {
setUserValue(inputRef.current.value);
setTimeout(() => {setOutput(inputRef.current.value)}, 700) }

希望这会对你有所帮助。

【讨论】:

  • 感谢您的回复。这可以解决大部分问题。但是,如果用户按住箭头(速度非常快)超过 700 毫秒,那么即使用户仍未停止,输出也会更新。
  • 我将编辑我的问题以明确它不仅仅是从键盘输入。
【解决方案2】:

这应该可以解决问题。您需要使用 ref 来在重绘之间保持去抖动功能:

const { useCallback, useState, useRef } = React;
const { render } = ReactDOM;
const { debounce } = _;

function App() {
  const [userValue, setUserValue] = useState(0);
  const [output, setOutput] = useState(0);
  const debounced = useRef(debounce((value) => setOutput(value), 600))

  const updateUserValue = useCallback(({ target: { value }}) => {
    setUserValue(value);
    debounced.current(value)
  }, []);

  return (
    <div className="App">
      <div>
        <h1>Input</h1>
        <input
          value={userValue}
          type="number"
          onChange={updateUserValue}
        ></input>
      </div>
      <div>
        <h1>Output</h1>
        {output}
      </div>
    </div>
  );
}

render(<App />, document.body);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script>

【讨论】:

    【解决方案3】:

    你真的不需要 ref 来解决这个问题。拨打useEffect 就足够了:

    useEffect(() => {
      const timeout = setTimeout(() => {
        setOutput(userValue);
      }, 600);
      return () => {
        clearTimeout(timeout);
      };
    }, [userValue]);
    
    const updateOutput = (e) => {
      setUserValue(e.target.value);
    };
    

    【讨论】:

    • 我不知道 useEffect 返回的函数会在每次依赖项更改时被调用。我以为只有当整个组件卸载时。很有价值的一课。
    猜你喜欢
    • 2018-06-13
    • 1970-01-01
    • 2023-01-16
    • 2021-07-23
    • 2019-08-29
    • 1970-01-01
    • 1970-01-01
    • 2017-10-02
    • 1970-01-01
    相关资源
    最近更新 更多