skip to Main Content

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


  1. 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.

    Login or Signup to reply.
  2. Here is the difference on when to use them.

    useMemo

    It should be used when there are expensive calculations that needs to be done. (eg: assume you have a long list of calculations to be done which slows your js thread if there are multiple re-renders to the component).

    What you are referring to as wrapping of the component should be memo and not useMemo 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

    In react on every re-render all the variable and function inside it is re-computed hence the function reference changes everytime. If you pass it to a memo-ed component it will possibly re-render since the function reference changes. To avoid that you can use useCallback which will only change its reference on dependency change provided to it. Also note that if any value like any state is being used inside it which is not added to the dependency chain it will cause issue since the value won’t be updated and have stale values when the function gets called.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search