【问题标题】:debounce textarea input with react/redux使用 react/redux 去抖动 textarea 输入
【发布时间】:2017-09-28 07:02:03
【问题描述】:

我正在尝试使用 react/redux 去抖动 textarea 值并在 div#preview 中显示去抖动值,但在第一次函数调用后我收到合成事件警告。

我有用于处理按预期工作的 textarea 值状态的 reducer,但为简单起见,我在此 sn-p 中编写了本地状态。

如果除了debounce 之外还有更好的方法来避免在每个keypress 之后重新渲染,我很想知道。提前致谢。

class TextArea extends React.Component {
  constructor(props){
    super(props);
    this.state = {foo: ''}
  }
  
  handleInputChange = _.debounce((e) => {
    e.persist();
    let value = e.target.value;
    this.setState({foo: value});
  }, 300);

  render() {
    return (
      <div>
       <textarea onChange={(e)=>this.handleInputChange(e)} value={this.state.foo}></textarea>
       <p id="preview">{this.state.foo}</p>
      </div>
    );
  }
}


ReactDOM.render(
  <TextArea />,
  document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

【问题讨论】:

  • 您的值基于状态,但只会以去抖动的方式更新...这对您有用吗?为什么需要去抖动?
  • 如上所述,我有一个preview div,如果在textarea 中有一个长按键或类似 100 多个字符,则由于不断的重新渲染,反应开始变得迟钝。是的,在给定的去抖时间警告出现后,去抖只工作一次。对无效值做出反应警告:This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property target on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist().
  • 我在处理 redux 周期时遇到了延迟。在组件的本地状态上,我还没有看到这种情况发生......你能在小提琴中重现吗?
  • 嗯,确实是一样的。预计当地会出现类似情况。好吧,那么显然它与 redux 循环有关。首先猜测这是由于对道具的不断映射状态和对道具的不断分派,所以也许我可以将映射状态反跳到道具:o
  • 我建议重新考虑架构,也许您不需要 redux 商店中的价值?

标签: javascript reactjs redux lodash


【解决方案1】:

您收到此错误是因为您尝试在debounce 的超时内处理.persist() 事件。当超时调用回调时,合成事件已经被释放。所以你必须在 debounce 之外保持事件。

但是,您的想法还有另一个问题。由于文本框是一个受控组件,因此对更新的值进行去抖动会导致文本框仅在使用的停止输入后才呈现(部分)文本。

为了防止您需要立即更新受控元素的状态,并为显示状态(或 redux 操作调度)的更新去抖动。

例如:

class TextArea extends React.Component {
  constructor(props){
    super(props);
    this.state = { foo: '', controlled: '' }
  }
  
  updateFoo = _.debounce((value) => { // this can also dispatch a redux action
    this.setState({foo: value});
  }, 300);
  
  handleInputChange = (e) => {
    const value = e.target.value;
    
    this.setState({
      controlled: value
    });
    
    this.updateFoo(value);
  }

  render() {
    return (
      <div>
       <textarea onChange={ this.handleInputChange }
       value={this.state.controlled} />
       <p id="preview">{this.state.foo}</p>
      </div>
    );
  }
}


ReactDOM.render(
  <TextArea />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="react"></div>

【讨论】:

  • 哦,我知道魔法从何而来了。不过,由于存在第二个状态controlled,这是否意味着我必须将第二个状态添加到用于更新 textarea 值的减速器或单独的减速器?除非我必须在本地设置 controlled 状态。
  • 您可以在本地设置controlled 状态,因为它只需要更新textarea,您将在debounce 中调度将更新商店的操作。
【解决方案2】:

另一个答案已经涵盖了持久化事件的问题。对于输入去抖动方面,您可能需要阅读我的博文Practical Redux, Part 7: Form Change Handling。在那篇文章中,我展示了一个可重用的组件,它可以为应用程序的其余部分处理去抖动文本输入更新,同时允许它们立即使用当前值重新渲染。

【讨论】:

  • 请提供实际解释,而不是提供外部链接。如果文章消失,它不仅可以避免您的答案变得过时,而且还有助于缩小冗长文章中实际有趣的部分。 :)
【解决方案3】:

我为此付出了很多努力,但自己无法获得令人满意的结果。最终,我使用了https://github.com/nkbt/react-debounce-input,它运行良好,并且比我之前失败的尝试简单得多。

  /**
   * Updates the current node "text" value.
   *
   * @param event
   */
  const onTextInputValueChange = (event: any) => {
    const newValue = event.target.value;
    patchCurrentNode({
      data: {
        text: newValue,
      },
    } as InformationNodeData);
  };
    

  <DebounceInput
          element={'textarea'}
          debounceTimeout={500}
          placeholder={'Say something here'}
          onChange={onTextInputValueChange}
          value={node?.data?.text}
        />

patchCurrentNode 写入我的 Recoil 存储。

DebounceInput 组件处理内部状态以显示最新值,同时仅偶尔更新存储中的值一次。

实现并不特定于 Recoil,使用 Redux 很可能会像魅力一样工作。

【讨论】:

    猜你喜欢
    • 2016-07-17
    • 2013-03-10
    • 1970-01-01
    • 2018-01-12
    • 2017-05-03
    • 2021-01-03
    • 1970-01-01
    • 2019-03-01
    • 2019-12-14
    相关资源
    最近更新 更多