【问题标题】:Debounce inside functional component in React.jsReact.js 中的功能组件内部去抖动
【发布时间】:2020-07-16 15:16:11
【问题描述】:

尝试在具有随更改更新的输入字段的反应组件上创建延迟。

代码如下:

import React from 'react'; 
import Input from "@material-ui/core/Input";
import {debounce} from "lodash";
...

const SampleRow = () => {
    let [newFriend, setNewFriend] = React.useState({name: '', lastSeen: '', contactIn: ''});

    const onAddFriend = debounce(() => {
        if(newFriend.name.length === 0){
            return;
        }
        console.log(newFriend.name);
    }, 2000);

    return (
        <TableRow>
            <TableCell component="th" scope="row">
                <Input
                    value={newFriend.name}
                    onChange={(event) => {
                        setNewFriend({...newFriend, name: event.target.value});
                        onAddFriend();
                    }}
                    placeholder={"Add friend"}
                    disableUnderline={true}
                />
            </TableCell>
            <TableCell align="left">{newFriend.lastSeen ? newFriend.lastSeen : ''}</TableCell>
            <TableCell align="left">{newFriend.contactIn ? newFriend.contactIn : ''}</TableCell>
            <TableCell align="left" className={classes.externalLinks}/>
        </TableRow>
    )
};

发生的情况是console.log 打印出每个更改(超时 2 秒后),并且不会取消之前的触发器。

问题是为什么会这样?又该如何解决呢?

我发现与复杂的useDebounce 逻辑或类组件类似的问题。没有找到这个问题的参考。

【问题讨论】:

  • 您必须清除已调用的去抖动功能。
  • 这不就是debounce的全部概念吗?调用函数应该重置time-interval
  • 我认为函数的渲染可能会导致问题。我试过event.persist(),但不是这样。
  • 这正是您找到的解决方案“复杂”的原因。使用钩子,您需要使用useRef 来存储/处理/引用回调。这就是现有的钩子(如 useDebouncedCallback)处理它的方式。 useRef 上有一个很棒的 article,具有 useInterval 等功能。

标签: javascript reactjs react-hooks lodash debounce


【解决方案1】:

这里的问题是每次渲染都会重新创建您的去抖动函数。

解决此问题的一种方法是将其移出功能组件。另一种方法是将您的组件更改为 Class 组件并将您的去抖动函数保存为属性。

const { useState } = React
const { debounce } = _;

const onAddFriend = debounce(console.log, 2000);

const App = () => {
 
  const [newFriend, setNewFriend] = useState({name: '',   lastSeen: '', contactIn: ''});
  
  const handleInput = (event) => {
    setNewFriend({...newFriend, name: event.target.value});
    onAddFriend(event.target.value);
  }
  
  return (
    <div className="box">
      <form className="form">
        <div class="field">
          <label for="name-1">Update  {newFriend.name}</label>
          <div class="control">
            <input type="text" name="name-1" value={newFriend.name}
                    onChange={handleInput} class="input"/>
          </div>
        </div>
      </form>
    </div>
  )
}

ReactDOM.render(<App />,
document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.19/lodash.min.js"></script>
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.production.min.js"></script>

<div id="root"></div>

【讨论】:

  • 我后来认为重新创建函数可能会导致问题,但并没有将其导出为外部函数以进行解决 - 很好,简单,并且按预期工作 - 谢谢!
【解决方案2】:

Ciao,尝试取消之前的触发:

import React from 'react'; 
import Input from "@material-ui/core/Input";
import {debounce} from "lodash";
...

const SampleRow = () => {
  let [newFriend, setNewFriend] = React.useState({name: '', lastSeen: '', contactIn: 
''});

const onAddFriend = debounce((abort) => {
    if(abort){
        return;
    }
    console.log(newFriend.name);
}, 2000);

return (
    <TableRow>
        <TableCell component="th" scope="row">
            <Input
                value={newFriend.name}
                onChange={(event) => {
                    setNewFriend({...newFriend, name: event.target.value});
                    let abort = false;
                    if(event.target.value.length === 0){
                      abort = true;
                    }
                    onAddFriend(abort);
                }}
                placeholder={"Add friend"}
                disableUnderline={true}
            />
        </TableCell>
        <TableCell align="left">{newFriend.lastSeen ? newFriend.lastSeen : ''}</TableCell>
        <TableCell align="left">{newFriend.contactIn ? newFriend.contactIn : ''}</TableCell>
        <TableCell align="left" className={classes.externalLinks}/>
    </TableRow>
)
  };

【讨论】:

  • 它不起作用。最后一个触发器被取消,但其余触发器仍处于活动状态(并在给定持续时间后打印到控制台)。
  • 好的,我知道的另一个解决方案是带有标志的解决方案。我会在一段时间内更新我的答案。
  • 不幸的是它不起作用。我确实明白它背后的想法,但我的猜测是重新渲染 onAddFriend 函数会导致它错过标志。
【解决方案3】:

试试这个。它将始终检查队列中现有的去抖,并取消它找到任何。 参考:How to cancel a debounced function after it is called and before it executes?

【讨论】:

    猜你喜欢
    • 2021-06-30
    • 2015-03-03
    • 1970-01-01
    • 2021-05-25
    • 2021-01-26
    • 2018-02-09
    相关资源
    最近更新 更多