使用 Subjects 的 React 文本输入组件上的 Rxjs 去抖不在 stateless/functional 组件上批量输入文本

Rxjs debounce on react text input component using Subjects does not batch input text on stateless/functional component

我正在尝试更深入地研究 rxjs,发现一个问题,即我尝试去抖动的输入字段会在每次按键时分派一个事件,去抖动仅保留输出但会导致树状结构如下:

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

相同的代码在 class 组件中按预期工作() but cannot understand why it doesn't work with stateless components. Here's an example: https://stackblitz.com/edit/react-hzhrmf - 您可以看到每次击键都会触发 useState 更新。

非常感谢。

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'));

这是自定义 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,"");