【问题标题】:Rxjs debounce on react text input component using Subjects does not batch input text on stateless/functional component使用主题的 Rxjs 对反应文本输入组件的去抖动不会在无状态/功能组件上批量输入文本
【发布时间】:2020-02-19 14:00:14
【问题描述】:

我正在尝试更深入地研究 rxjs,并发现了一个问题,即我尝试去抖动的输入字段在每次按键时都会调度一个事件,去抖动只保留输出,但会生成如下所示的树:

a
as(delay - waits 200ms, then fires the rest synchronously)
asd
asdf
asdfg 
....

相同的代码在类组件中按预期工作(https://stackoverflow.com/a/44300853/1356046),但无法理解为什么它不适用于无状态组件。这是一个示例:https://stackblitz.com/edit/react-hzhrmf - 您可以看到每次击键都会触发 useState 更新。

非常感谢。

【问题讨论】:

    标签: javascript rxjs debouncing


    【解决方案1】:

    这是一个自定义 Hook 版本,因为它可能会在一个表单中多次使用,否则会使您的代码混乱。

    function useDebounce<T = any>(time: number, defaultValue: T): [T, (v: T) => void] {
      let [value, setValue] = React.useState<T>(defaultValue);
      let [value$] = React.useState(() => new Subject<T>());
      React.useEffect(() => {
        let sub = value$.pipe(debounceTime(time)).subscribe(setValue);
        return () => sub.unsubscribe();
      }, [time, value$]);
     return [value, (v) => value$.next(v)];
    }
    
    //useage: 
    let [value,setValue] = useDebounce(200,"");
    

    【讨论】:

      【解决方案2】:

      React 不断调用你的函数来渲染组件。因此,主题不断被重新创建。

      使用带有 useState 的工厂来保留主题并使用 useEffect 确保订阅只进行一次应该可以解决您的问题。

      类似这样的:

      import React, { Component, useState, useEffect, useRef } from 'react';
      import { render } from 'react-dom';
      import { debounceTime, map, tap, distinctUntilChanged } from 'rxjs/operators';
      import { fromEvent, Subject } from 'rxjs';
      
      import './style.css';
      const App = props => {
        const [queryName, setQueryName] = useState("");
        const [debouncedName, setDebouncedName] = useState("");
        const [onSearch$] = useState(()=>new Subject());
        useEffect(() => {
          const subscription = onSearch$.pipe(
            debounceTime(400),
            distinctUntilChanged(),
            tap(a => console.log(a))
          ).subscribe(setDebouncedName);
        }, [])
        const handleSearch = e => {
          setQueryName(e.target.value);
          onSearch$.next(e.target.value);
        };
      
        return (
          <div>
            <input
              placeholder="Search Tags"
              value={queryName}
              onChange={handleSearch}
            />
            <p>Debounced: {debouncedName}</p>
          </div>
        );
      }
      
      render(<App />, document.getElementById('root'));
      

      【讨论】:

      • 感谢您的解释,现在完全有道理,我尝试将主题移出组件,但遇到了同样的问题,这让我相信重新订阅是罪魁祸首。为了更深入的了解,我会多玩一点。 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-10
      • 2021-06-30
      • 2018-05-15
      • 2019-04-16
      • 1970-01-01
      • 2021-05-09
      • 2021-09-14
      相关资源
      最近更新 更多