FlatList的renderItem应该用react useCallback挂钩包装吗?

时间:2020-03-23 19:48:33

标签: javascript reactjs react-native react-hooks

const Component = React.memo(props => {
  const { url } = props;

  const keyExtractor = useCallback(item => item.id, []);

  const handleClick = useCallback(() => {
    Linking.openURL(url);
  }, [url]);

  const renderItem = useCallback(({ item }) => {
    return (
      <TouchableOpacity onPress={handleClick}>
        <Text>Test</Text>
      </TouchableOpacity>
    );
  }, [handleClick]);

  return (
    <FlatList
      data={data}
      keyExtractor={keyExtractor}
      renderItem={renderItem}
    />
  );
});

就像上面的代码一样。

如果renderItem函数用useCallback挂钩包装。它必须检查handleClick函数引用是否已更改,因为如果handleClick不同,则url函数可能会更改。看起来有点奇怪。

我想知道使用useCallback来包装renderItem的性能是否有显着差异。

renderItem函数使用钩子的最佳实践是什么?谢谢

1 个答案:

答案 0 :(得分:1)

由于您已经使用过React.memo,除非更改URL,否则组件将不会执行,如果更改了URL,则useCallback仍将重新创建函数,因此您可以将其保留,前提是这是唯一可以更改的道具。

这里有一些代码演示了这一点,您可以根据需要重新渲染App,但除非更改url,否则它不会重新渲染其他组件。

const SubComponent = ({ onClick }) => {
  console.log('sub component render');
  return <button onClick={onClick}>log url</button>;
};

const PureComponent = React.memo(function PureComponent({
  url,
}) {
  console.log('pure component render', url);
  //even if I do React.useCallback(fn,[url]) that would mean
  //  it creates onClick when url changes but it would already
  //  only create onClick when url changes because memo will
  //  memoize the component result and not execute PureComponent
  //  unless the url changes
  const onClick = () => console.log('url is', url);
  return <SubComponent onClick={onClick} />;
});

const App = () => {
  const [, reRenderApp] = React.useState({});
  const [url, setUrl] = React.useState(
    new Date().toUTCString()
  );
  console.log('rendering App');
  return (
    <div>
      <button onClick={() => reRenderApp({})}>
        re render app
      </button>
      <button
        onClick={() => setUrl(new Date().toUTCString())}
      >
        set url
      </button>
      <PureComponent url={url} />
    </div>
  );
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>