Raul
Raul

Reputation: 3081

React - useCallback vs useMemo for JSX elements

I have implemented this component:

function CardList({
  data = [],
  isLoading = false,
  ListHeaderComponent,
  ListEmptyComponent,
  ...props
}) {
  const keyExtractor = useCallback(({ id }) => id, []);

  const renderItem = useCallback(
    ({ item, index }) => (
      <Card
        data={item}
        onLayout={(event) => {
          itemHeights.current[index] = event.nativeEvent.layout.height;
        }}
      />
    ),
    []
  );

  const renderFooter = useCallback(() => {
    if (!isLoading) return null;

    return (
      <View style={globalStyles.listFooter}>
        <Loading />
      </View>
    );
  }, [isLoading]);

  return (
    <FlatList
      {...props}
      data={data}
      keyExtractor={keyExtractor}
      renderItem={renderItem}
      ListHeaderComponent={ListHeaderComponent}
      ListFooterComponent={renderFooter()}
      ListEmptyComponent={ListEmptyComponent}
    />
  );
}

As my CardList component is heavy, I have tried to optimize it following these tips.

But, I think that instead of using useCallback for renderFooter, I should use useMemo, in order to memoize the resulted JSX and not the method:

const ListFooterComponent = useMemo(() => {
  if (!isLoading) return null;

  return (
    <View style={globalStyles.listFooter}>
      <Loading />
    </View>
  );
}, [isLoading]);

Am I correct?

Upvotes: 2

Views: 4308

Answers (3)

Giorgi Moniava
Giorgi Moniava

Reputation: 28654

In general useMemo is used to cache the result of a calculation between re-renders; seems that is more appropriate for your case because you are calling renderFooter and passing its result as props.

Note there is also a possibility to optimize rendering using useMemo, it is highlighted in the docs:

function Parent({ a, b }) {
  // Only re-rendered if `a` changes:
  const child1 = useMemo(() => <Child1 a={a} />, [a]);
  // Only re-rendered if `b` changes:
  const child2 = useMemo(() => <Child2 b={b} />, [b]);
  return (
    <>
      {child1}
      {child2}
    </>
  )
}

The reason why it works here is probably due to the fact that when during a re-render, react sees reference to the same react element in the same place in component hierarchy, it skips re-rendering it. This happens here because by using useMemo to create child1 and child2 elements, we ensure that the same element reference is returned on subsequent renders as long as its dependencies have not changed.

useCallback lets you cache a function definition.

Upvotes: 3

Ben Clayton
Ben Clayton

Reputation: 82219

useMemo is perfectly sensible here as it memoizes the result (the JSX). As you say, useCallback memoizes the function not the result.

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074228

"Should" is a matter of opinion, but you certainly can. React elements are fully reusable. It's not likely to make any real difference to your component, though, since creating the elements is fast and renderFooter is just used immediately in a component that's already running (unlike keyExtractor and renderItem, which you're passing to FlatList, so you want to make them stable where possible so FlatList can optimize its re-rendering). But you certainly can do that.

Upvotes: 0

Related Questions