3

There is a pattern that I use often that I feel must be an anti-pattern but I don't know a better alternative.

Occasionally my components may receive one or more events that mean that a re-render is necessary. Sometimes it is the case that I do not know how many times the event handlers will be called. To prevent multiple re-renders as a result of many calls to the handlers I do something like this:

 _myEventHandler() { // may be called multiple times between renders
   if (!this._updateQueued) {
     this._updateQueued = true;
     this._updateTimer = setTimeout(() => {
       this._updateQueued = false;
       this.forceUpdate();
     }, 0);
   }
 }

The problem here is I feel it can't be performant due to the latency between code stopping and the event loop starting.

A real world example of this is when I am using react-visibility-sensor and I have multiple elements change their visibility at once, I don't want to re-render for each element, instead I want just one re-render once all the updates have been received.

Is there another better way to deal with multiple calls?

BTW: if you are going to use the above hack don't forget to call clearTimout(this._updateQueued) in your componentWillUnmount

Jack Allan
  • 14,554
  • 11
  • 45
  • 57

2 Answers2

1

A debounce will reduce the number of times a certain piece of code is run, regardless of how often it is called. Here is a rather simple implementation.

const debounce = (callable, time) => {
  let timeout;

  return function() {
    const functionCall = () => callable.apply(this, arguments);

    clearTimeout(timeout);
    timeout = setTimeout(functionCall, time);
  };
};

And this is how to use it.

const debouncedIteration = debouce(() => {
   console.log("Iteration"); // This will be called once every 1000 milliseconds.
}, 1000);

while (true) {
   debouncedIteration();
}
Francis Malloch
  • 1,074
  • 9
  • 20
  • Yes, quite possibly @JackAllan except a bit more standardized, the majority of React developers know what a debounce is. – Francis Malloch Mar 06 '18 at 02:08
  • Using a debouncer to cut down on re-renders is not an anti-pattern; in fact it's a common strategy for dealing with the sluggishness you often encounter with controlled inputs, to name just one example. – brogrammer Mar 08 '18 at 16:28
1

You can avoid re-renders using this lifecycle method shouldComponentUpdate (as also mentioned by @fungusanthrax). Keep this inside your react component :

shouldComponentUpdate(nextProps, nextState) {
  return !isEqual(this.props, nextProps) || !isEqual(this.state, nextState);
}

using isEqual from lodash here, make sure to include it. This will only re-render your component when there's a change in props or state value.

To install lodash:

npm install -S lodash

and import isEqual in your component file :

import isEqual from 'lodash/isEqual';
Fawaz
  • 3,404
  • 3
  • 17
  • 22