尽管使用了备忘录并且没有更改任何props,但对功能组件中的子项做出react

IT技术 reactjs react-native react-hooks
2022-07-22 00:36:13

我有一个图标组件,它绘制一个图标并且它正在闪烁,因为父级让它毫无意义地重新渲染。我不明白为什么会发生这种情况以及如何防止这种情况发生。

这是一个说明问题的小吃。

我们使用 setInterval 模拟父级更改。

我们通过在控制台中记录“重新渲染”来模拟图标重新渲染。

这是代码:

import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';


// or any pure javascript modules available in npm
let interval = null

const Child = ({name}) => {
  //Why would this child still rerender, and how to prevent it?
  console.log('rerender')
  return <Text>{name}</Text>
}
const ChildContainer = ({name}) => {
  const Memo = React.memo(Child, () => true)
  return <Memo name={name}/>
}

export default function App() {
  const [state, setState] = React.useState(0)
  const name = 'constant'
  // Change the state every second
  React.useEffect(() => {
    interval = setInterval(() => setState(s => s+1), 1000)
    return () => clearInterval(interval)
  }, [])
  return (
    <View>
      <ChildContainer name={name} />
    </View>
  );
}

如果您能向我解释为什么会发生这种情况以及修复它的正确方法是什么,那就太棒了!

1个回答

如果您移出const Memo = React.memo(Child, () => true)ChildContainer的代码将按预期工作。

虽然ChildContainer不是记忆组件,但它将被重新渲染并Child在每次父重新渲染时创建一个记忆组件。

通过将 memoization 移到 之外ChildContainer,您可以安全地记住您的组件一次,并且无论调用Child多少次,都只会运行一次。ChildContainerChild

这是一个工作演示我还在 上添加了一个日志App来跟踪每次重新渲染,并在 上添加一个日志,ChildComponent这样您就可以看到在每次重新渲染时都会调用此函数,而无需再实际接触Child

您也可以Child直接包装React.memo

import * as React from "react";
import { Text, View, StyleSheet } from "react-native";

// or any pure javascript modules available in npm
let interval = null;

const Child = React.memo(({ name }) => {
  //Why would this child still rerender, and how to prevent it?
  console.log("memoized component rerender");
  return <Text>{name}</Text>;
}, () => true);

const ChildContainer = ({ name }) => {
  console.log("ChildContainer component rerender");
  return <Child name={name} />;
};

export default function App() {
  const [state, setState] = React.useState(0);
  const name = "constant";
  // Change the state every second
  React.useEffect(() => {
    interval = setInterval(() => setState(s => s + 1), 1000);
    return () => clearInterval(interval);
  }, []);

  console.log("App rerender");
  return (
    <View>
      <ChildContainer name={name} />
    </View>
  );
}