[SOLVED] React Hooks: skip re-render on multiple consecutive setState calls

Table of Contents

Issue

Suppose I have the following code: (which is too verbose)

function usePolicyFormRequirements(policy) {
  const [addresses, setAddresses] = React.useState([]);
  const [pools, setPools] = React.useState([]);
  const [schedules, setSchedules] = React.useState([]);
  const [services, setServices] = React.useState([]);
  const [tunnels, setTunnels] = React.useState([]);
  const [zones, setZones] = React.useState([]);
  const [groups, setGroups] = React.useState([]);
  const [advancedServices, setAdvancedServices] = React.useState([]);
  const [profiles, setProfiles] = React.useState([]);

  React.useEffect(() => {
    policiesService
      .getPolicyFormRequirements(policy)
      .then(
        ({
          addresses,
          pools,
          schedules,
          services,
          tunnels,
          zones,
          groups,
          advancedServices,
          profiles,
        }) => {
          setAddresses(addresses);
          setPools(pools);
          setSchedules(schedules);
          setServices(services);
          setTunnels(tunnels);
          setZones(zones);
          setGroups(groups);
          setAdvancedServices(advancedServices);
          setProfiles(profiles);
        }
      );
  }, [policy]);

  return {
    addresses,
    pools,
    schedules,
    services,
    tunnels,
    zones,
    groups,
    advancedServices,
    profiles,
  };
}

When I use this custom Hook inside of my function component, after getPolicyFormRequirements resolves, my function component re-renders 9 times (the count of all entities that I call setState on)

I know the solution to this particular use case would be to aggregate them into one state and call setState on it once, but as I remember (correct me, if I’m wrong) on event handlers (e.g. onClick) if you call multiple consecutive setStates, only one re-render occurs after event handler finishes executing.

Isn’t there any way I could tell React, or React would know itself, that, after this setState another setState is coming along, so skip re-render until you find a second to breath.

I’m not looking for performance-optimization tips, I’m looking to know the answer to the above (Bold) question!

Or do you think I am thinking wrong?

Thanks!

————–


UPDATE
How I checked my component rendered 9 times?

export default function PolicyForm({ onSubmit, policy }) {
  const [formState, setFormState, formIsValid] = usePgForm();
  const {
    addresses,
    pools,
    schedules,
    services,
    tunnels,
    zones,
    groups,
    advancedServices,
    profiles,
    actions,
    rejects,
    differentiatedServices,
    packetTypes,
  } = usePolicyFormRequirements(policy);

  console.log(' --- re-rendering'); // count of this
  return <></>;
}

Solution

I thought I’d post this answer here since it hasn’t already been mentioned.

There is a way to force the batching of state updates. See this article for an explanation. Below is a fully functional component that only renders once, regardless of whether the setValues function is async or not.

import React, { useState, useEffect} from 'react'
import {unstable_batchedUpdates} from 'react-dom'

export default function SingleRender() {

    const [A, setA] = useState(0)
    const [B, setB] = useState(0)
    const [C, setC] = useState(0)

    const setValues = () => {
        unstable_batchedUpdates(() => {
            setA(5)
            setB(6)
            setC(7)
        })
    }

    useEffect(() => {
        setValues()
    }, [])

    return (
        <div>
            <h2>{A}</h2>
            <h2>{B}</h2>
            <h2>{C}</h2>
        </div>
    )
}

While the name "unstable" might be concerning, the React team has previously recommended the use of this API where appropriate, and I have found it very useful to cut down on the number of renders without clogging up my code.

Answered By – Andrew Einhorn

Answer Checked By – Terry (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *