简体   繁体   English

React - JSX 元素的 useCallback 与 useMemo

[英]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 .由于我的 CardList 组件很重,我尝试按照这些提示对其进行优化。

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:但是,我认为我应该使用useCallback代替renderFooter ,以便useMemo结果 JSX 而不是方法:

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

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

Am I correct?我对么?

If you want to avoid expensive calculations use useMemo .如果您想避免昂贵的计算,请使用useMemo useCallback is for memoizing a callback/function . useCallback用于记忆回调/函数

Official docs do have an example of using useMemo with JSX for avoiding re render: 官方文档确实有一个将useMemo与 JSX 一起使用以避免重新渲染的示例:

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}
    </>
  )
}

Personal observation:个人观察:

But to be more detailed, it seems it is not that useMemo here magically prevents re render, but the fact that you render same reference on the same spot in component hierarchy , makes react skip re render.但更详细地说,似乎并不是这里的useMemo神奇地阻止了重新渲染,而是您在组件层次结构中的同一位置渲染相同的引用这一事实,使得 react 跳过重新渲染。

Here:这里:

let Child = (props) => {
  console.log('rendered', props.name);

  return <div>Child</div>;
};

export default function Parent() {
  let [a, setA] = React.useState(0);
  let [b, setB] = React.useState(0);

  // Only re-rendered if `a` changes:
  const child1 = React.useMemo(() => <Child a={a} name="a" />, [a]);
  // Only re-rendered if `b` changes:
  const child2 = React.useMemo(() => <Child b={b} name="b" />, [b]);

  return (
    <div
      onClick={() => {
        setA(a + 1);
      }}
    >
      {a % 2 == 0 ? child2 : child1}
    </div>
  );
}

If you keep clicking the div you can see it still prints "rendered b" even though we never changed the b prop.如果您继续单击div ,您可以看到它仍然打印“rendered b”,即使我们从未更改b属性。 This is because react is receiving a different reference each time inside the div , and doesn't optimize the re render - like it does if you had simply rendered only child2 without that condition and child1 .这是因为 react 每次在div内接收到不同的引用,并且不会优化重新渲染 - 就像您仅渲染child2而没有该条件和child1时所做的那样。

Note: The fact that react skips rendering when it receives same element reference in the same spot is known , so apparently useMemo technique works because of that when it comes to rendering optimization.注意:当 React 在同一位置接收到相同的元素引用时,React 会跳过渲染这一事实是众所周知的,因此显然useMemo技术在渲染优化方面起作用。

"Should" is a matter of opinion, but you certainly can . “应该”是一个见仁见智的问题,但你当然可以 React elements are fully reusable. React 元素是完全可重用的。 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).但是,它不太可能对您的组件产生任何真正的影响,因为创建元素很快,并且renderFooter只是在已经运行的组件中立即使用(与您传递给FlatListkeyExtractorrenderItem不同,所以您想要尽可能使它们稳定,以便FlatList可以优化其重新渲染)。 But you certainly can do that.但你当然可以做到。

useMemo is perfectly sensible here as it memoizes the result (the JSX). useMemo在这里非常明智,因为它会记住结果(JSX)。 As you say, useCallback memoizes the function not the result.正如您所说, useCallback记住函数而不是结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM