Rxjs устраняет дребезг на компоненте ввода текста с использованием субъектов, не вводит текст в пакетном режиме на функциональном компоненте без состояния

Я пытаюсь глубже погрузиться в rxjs и обнаружил проблему, из-за которой поле ввода, которое я пытаюсь отменить, отправляет событие при каждом нажатии клавиши, debounce содержит только вывод, но приводит к такому дереву:

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 срабатывает при каждом нажатии клавиши.

Большое спасибо.


person SinSync    schedule 23.10.2019    source источник


Ответы (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'));
person Boyen    schedule 23.10.2019
comment
Спасибо за объяснение, теперь это имеет смысл, я попытался удалить тему из компонента, но столкнулся с той же проблемой, которая заставляет меня поверить, что повторная подписка является основным виновником. Я поиграю еще немного для более глубокого понимания. :) - person SinSync; 23.10.2019

Вот версия пользовательского хука, так как он может использоваться в форме несколько раз и в противном случае может загромождать ваш код.

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,"");
person Rafael Dabrowski    schedule 05.05.2020