使用 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,"");
我正在尝试更深入地研究 rxjs,发现一个问题,即我尝试去抖动的输入字段会在每次按键时分派一个事件,去抖动仅保留输出但会导致树状结构如下:
a
as(delay - waits 200ms, then fires the rest synchronously)
asd
asdf
asdfg
....
相同的代码在 class 组件中按预期工作(
非常感谢。
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,"");