So, from what I understand from the official React docs, when trying to prevent a Child component of a certain Parent to re-render, you need to wrap the child in a useMemo, then wrap the functions inside the Parent, that are passed down to the Child in a useCallback, that being said, what is the point of this implementation:
const ParentComponent = () => {
const [isFiltersOpen, setIsFiltersOpen ] = useState(false);
const hasNoDevices = someHookReturnFromApi();
const hasDevices = someHookReturnFromApi();
const areAllDevicesLoaded = anotherHookButCallsApiForData();
const missingOtherStuff = thisHookChecksIfSomeApisAreDoneLoading();
const onSomething = useCallback(() => console.log(hasNoDevices), [hasNoDevices]);
const sortedData = useMemo(() => hasDevices.sort(// do some sorts...));
const filters = filtersFromApi();
const onClosePanel = () => setIsFiltersOpen(false);
const renderSection = useCallback(() => {
if (hasNoDevices) {
return (
<EmptyState
areAllDevicesLoaded={areAllDevicesLoaded}
subtitle={t('just.a.translation')}
/>
);
}
if (missingOtherStuff) {
return (
<SectionEmptyState onSomething={onSomething} />
);
}
return (
<>
<SectionData
sortedData={sortedData}
hasDevices={hasDevices}
/>
<div>
{isFiltersOpen && <Panel isOpen={isOpen} filters={filters} onClose={onClosePanel} />}
</div>
</>
);
}, [hasDevices, hasNoDevices, sortedData, areAllDevicesLoaded, onSomething, isFiltersOpen, isOpen, filters, onClosePanel]);
return (
<>
<h1>Parent component</h1>
{renderSection()}
</>
);
I mean useCallback, returns the same function, same reference, so how does this help? Does it have something to do with memory allocation? So that when the parent re-renders, another space in the memory won’t be taken up to create another reference to all of that?
Best regards and happy coding (reading materials are always appreciated!)
2
Answers
useCallback caches your function and its dependencies so that it won’t re-render when said dependencies haven’t changed (following renders compare current to previous dependencies). So yes, this is a simple performance optimization.
This also exactly what is happening in your example:
renderSection
will stay the same until one or more of[hasDevices, hasNoDevices, sortedData, areAllDevicesLoaded, onSomething, isFiltersOpen, isOpen, filters, onClosePanel]
have changed.You could think about implementing memo https://react.dev/reference/react/memo to not only cache the function but also skip re-rendering of
{renderSection()}
entirely when the function hasn’t changed.Here is the difference on when to use them.
useMemo
What you are referring to as wrapping of the component should be
memo
and notuseMemo
which when used without any equality parameter shallow compares the props of the component wrapped with it and based on the comparison returns the previous result (no-rerender) if there is a change it re-renders the said comparison.useCallback