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 setState
s, 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 <></>;
}
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.