Rxjs debounce on react text input component

后端 未结 3 802
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-16 06:41

I have the following react component



        
相关标签:
3条回答
  • 2020-12-16 07:03

    You will need to cretae observable from change events(for example using Subject) and then debounce on that.

    Here is the fully featured example for you:

    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>

    0 讨论(0)
  • 2020-12-16 07:08

    This would be a good use case for Refract!

    The first step would be to pull the input out into a separate component:

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

    Next step would be to wrap this component with Refract's withEffects higher-order component, with a handler and an aperture to handle the side-effects like this:

    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)
    

    An aperture lets you observe your component's props. In this case, it would make sense to observe the value prop - every time the value changes, the component.observe('value') stream gets a new value.

    The handler is a function called with each value output by the aperture's stream. In this case, the debounced value is passed straight through to a new prop called onUpdate.

    Both apertures and handlers are explained in detail in the docs - Observing React introduces apertures, and Handling Effects explains handlers.

    As an example of how you would use this:

    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>
            )
        }
    }
    

    With this code, the text DebouncedInput would display the user's input instantly (which is ideal for UX), while debouncing the side-effect of calling the onUpdate callback. It would then be trivial to expose this onUpdate to components which consume the Search component!

    0 讨论(0)
  • 2020-12-16 07:14

    I agree with the example by Oles Savluk. In addition, I would extract the Subject logic out of the component. It doesn't need to live inside the component, as it has no state, and I think this also makes the component easier to understand.

    Also: The example is updated to use 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>

    0 讨论(0)
提交回复
热议问题