Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to debounce user input in reactjs using rxjs

Tags:

reactjs

rxjs

My problem may be a trivial one but I wasn't able to find the answer so far.

How can I defer (debounce) updating state in React while user is typing, to avoid unnecessary updates?

Having <input onChange={this.onChange} .../>, how can I bind onChange event with rxjs? Should I try to make this input observable or should I use FromEventPattern?

In both cases I have no idea how to bind React events with rxjs. The second question is whether the user will see any input changes during debounce?

like image 498
Pawel Stadnicki Avatar asked May 19 '16 07:05

Pawel Stadnicki


People also ask

How do you use debounce in react JS?

In the case of Debouncing, the API will trigger only once after 2 seconds, after we type our whole pin-code. First of all, create a state using the useState hook in React. const [pinCode, setPinCode] = React. useState("");

What is debounce time in RXJS?

debounceTime delays the values emitted by a source for the given due time. If within this time a new value arrives, the previous pending value is dropped and the timer is reset. In this way debounceTime keeps track of most recent value and emits that most recent value when the given due time is passed.

How do you debounce an event react?

You have 2 options to create debounced and throttled functions in React: using useCallback() or useMemo() hooks.


2 Answers

Solution #1

Using subjects:Fiddle

const state = new Rx.Subject()
              .debounceTime(1000)
              .scan((acc) => {
                return ++acc
              }, 0).do(::console.log)


const handler = (e) => {        
  state.next(e)
}

state.startWith(0).subscribe((clicks) => {
  ReactDOM.render(<button onClick={handler}>Clicked {clicks}</button>, document.querySelector('#app')) 
})

Solution #2

Using rxjs's fromEvent: Fiddle

// Intial render so element exists in dom (there is probably a better pattern)
ReactDOM.render( <button id='clickMe'>Click Me</button>, document.querySelector('#app')) 

const clicks = Rx.Observable
                .fromEvent(document.getElementById('clickMe'), 'click')
                .do(::console.log)
                .debounceTime(1000)
                .scan((acc) => {
                  return ++acc
                }, 0)

clicks.subscribe((clicks) => {
  ReactDOM.render( <button id='clickMe'>Click Me {clicks}</button>, document.querySelector('#app')) 
})

Solution #3

Note: highly experimental, and just something I tried to do for fun.

This is more for an action based architecture, where you have actions that change your state (flux). This is a handler that is fully standalone. It is used with a custom operator 'fromEventArgs': Fiddle (look at the console)

const handler = (e) => {        
  Rx.Observable
    .fromEventArgs(e, 'UniqueKey') 
    .debounceTime(1000)        
    .subscribe(x => console.log('Send an action', x))
}
like image 129
omerts Avatar answered Oct 05 '22 21:10

omerts


based on omerts propositions, (especially solution #1) here is my final code

input: Rx.Subject<any>;

constuctor(...){
   this.input = new Rx.Subject();
   this.input.debounce(1000).subscribe(this.processInput);
}

handleChange = event => {
   event.persist();
   this.input.onNext(event);

    };

processInput = x => {
   // invoke redux/flux action to update the state
}

render(){
   ...
   <input onChange={this.handleChange} ... />
   ...
}
like image 30
Pawel Stadnicki Avatar answered Oct 05 '22 23:10

Pawel Stadnicki