简体   繁体   English

Rxjs 使用主题在反应文本输入组件上去抖动不会在无状态/功能组件上批量输入文本

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

I'm trying to dive deeper into rxjs and found an issue where the input field I'm trying to debounce dispatches an event on every keypress, the debounce only holds the output but results in a tree like:我正在尝试更深入地研究 rxjs 并发现了一个问题,即我尝试去抖动的输入字段在每次按键时都会调度一个事件,去抖动仅包含 output 但结果如下所示的树:

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

The same code works as expected in a class component( https://stackoverflow.com/a/44300853/1356046 ) but cannot understand why it doesn't work with stateless components.相同的代码在 class 组件( https://stackoverflow.com/a/44300853/1356046 )中按预期工作,但无法理解为什么它不适用于无状态组件。 Here's an example: https://stackblitz.com/edit/react-hzhrmf - you can see the useState update fires for every keystroke.这是一个示例: https://stackblitz.com/edit/react-hzhrmf - 您可以看到每次击键都会触发 useState 更新。

Thanks a lot.非常感谢。

React continuously calls your function to render the component. React 不断调用你的 function 来渲染组件。 Therefore the Subject is continuously recreated.因此,主题不断被重新创建。

Using a factory with useState to keep the subject and working with useEffect to make sure the subscription is only made once should fix your issue.使用带有 useState 的工厂来保留主题并使用 useEffect 确保订阅只进行一次应该可以解决您的问题。

Something like this:像这样的东西:

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

Here is a version as custom Hook as this might be used multiple times in a form and can otherwise clutter your code.这是一个自定义 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,"");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM