【问题标题】:Rxjs debounce on react text input componentRxjs 在反应文本输入组件上去抖动
【发布时间】:2017-06-01 06:04:47
【问题描述】:

我有以下反应组件

<input className={styles.incSrchTextBox} type="text" name="search" placeholder="Search.."
   onChange={this.onChange} />


onChange(e) {
    const newText = e.target.value;
    console.log(newText);
    this.setState({ searchText: newText });
}

如何在 rxjs 上使用 debounce?

【问题讨论】:

    标签: reactjs rxjs


    【解决方案1】:

    您需要从更改事件中创建 observable(例如使用主题),然后对其进行去抖动。

    这是为您准备的全功能示例:

    class Search extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          search: '',
          debounced: '',
        };
        this.onSearch$ = new Rx.Subject();
        this.onSearch = this.onSearch.bind(this);
      }
      componentDidMount(){
        this.subscription = this.onSearch$
          .debounceTime(300)
          .subscribe(debounced => this.setState({ debounced }));
      }
      
      componentWillUnmount() {
        if (this.subscription) {
          this.subscription.unsubscribe();
        }
      }
      
      onSearch(e) {
        const search = e.target.value;
        this.setState({ search });
        this.onSearch$.next(search);
      }
    
      render() {
        const { search, debounced } = this.state;
        return (
          <div>
            <input type="text" value={search} onChange={this.onSearch} />
            <div>debounced value: {debounced}</div>
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <Search />,
      document.getElementById('root')
    );
    <script src="https://unpkg.com/rxjs@5.4.0/bundles/Rx.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="root"></div>

    【讨论】:

    • 不能在不创建主题的情况下做这样的事情 const example = Rx.Observable .fromEvent(input, 'keyup') .map(i => i.currentTarget.value); //在按键之间等待 0.5 秒以发出当前值 //丢弃所有其他值 const debouncedInput = example.debounceTime(500);
    • 是的,但是当你已经拥有 React 时直接使用 DOM 并不是一个好主意
    • 从 RxJs 6 开始你需要使用管道操作符来去抖动this.onSearch$.pipe(debounceTime(300)).subscribe(...)
    【解决方案2】:

    我同意 Oles Savluk 的示例。此外,我会从组件中提取主题逻辑。它不需要存在于组件内部,因为它没有状态,我认为这也使组件更容易理解。

    另外:示例已更新为使用 RxJS 6.2.2

    const { Subject } = rxjs;
    const { debounceTime } = rxjs.operators;
    
    const onSearch$ = new rxjs.Subject().pipe(
        debounceTime(300)
    );
    
    class Search extends React.Component {
      constructor(props) {
        super(props);
        
        this.state = {
          search: '',
          debounced: '',
        };
      }
    
      componentDidMount(){
        this.subscription = onSearch$.subscribe(
            debounced => this.setState({ debounced })
        );
      }
      
      componentWillUnmount() {
        if (this.subscription) {
          this.subscription.unsubscribe();
        }
      }
      
      onSearch = (e) => {
        const search = e.target.value;
        this.setState({ search });
        onSearch$.next(search);
      }
    
      render() {
        const { search, debounced } = this.state;
        return (
          <div>
            <input type="text" value={search} onChange={this.onSearch} />
            <div>debounced value: {debounced}</div>
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <Search />,
      document.getElementById('root')
    );
    <script src="https://unpkg.com/rxjs@6.2.2/bundles/rxjs.umd.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="root"></div>

    【讨论】:

      【解决方案3】:

      这对于Refract 来说是一个很好的用例!

      第一步是将输入拉出到一个单独的组件中:

      const Input = ({ onChange, value }) => (
          <input type="text" value={value} onChange={onChange} />
      )
      

      下一步将使用 Refract 的 withEffects 高阶组件包装此组件,并使用 handleraperture 来处理这样的副作用:

      import { withEffects } from 'refract-rxjs'
      import { debounceTime } from 'rxjs/operators'
      
      const Input = ({ onChange, value }) => (
          <input type="text" value={value} onChange={onChange} />
      )
      
      const aperture = () => component =>
          component.observe('value').pipe(debounceTime(300))
      
      const handler = ({ onUpdate }) => value => onUpdate(value)
      
      const DebouncedInput = withEffects(handler)(aperture)(Input)
      

      aperture 可让您观察组件的道具。在这种情况下,观察 value 属性是有意义的 - 每次 value 更改时,component.observe('value') 流都会获得一个新值。

      handler 是一个函数,由光圈流输出的每个值调用。在这种情况下,去抖值会直接传递给一个名为 onUpdate 的新道具。

      文档中详细解释了孔径和处理程序 - Observing React 介绍了孔径,Handling Effects 解释了处理程序。

      作为一个例子来说明你将如何使用它:

      class Search extends React.Component {
          state = { debounced: '', search: '' }
      
          onSearch = e => this.setState({ search: e.target.value })
          onUpdate = debounced => this.setState({ debounced })
      
          render() {
              return (
                  <div>
                      <DebouncedInput
                          type="text"
                          value={this.state.search}
                          onChange={this.onSearch}
                          onUpdate={this.onUpdate}
                      />
                      <div>debounced value: {debounced}</div>
                  </div>
              )
          }
      }
      

      使用此代码,文本 DebouncedInput 将立即显示用户的输入(这对于 UX 来说是理想的),同时消除调用 onUpdate 回调的副作用。然后将这个onUpdate 暴露给消耗Search 组件的组件将是微不足道的!

      【讨论】:

        猜你喜欢
        • 2020-02-19
        • 2018-05-15
        • 2021-04-14
        • 1970-01-01
        • 1970-01-01
        • 2017-07-10
        • 1970-01-01
        • 1970-01-01
        • 2019-12-21
        相关资源
        最近更新 更多