react-native-render-html:“你似乎在短时间内更新了 Y 组件的 X 属性......”

Jul*_*lph 32 react-native react-native-render-html

备注:我是 的作者react-native-render-html。此问题出于教育目的,符合 StackOverflow 政策

我在组件RenderHtml中渲染组件,WebDisplay如下所示:

import * as React from 'react';
import {ScrollView, StyleSheet, Text, useWindowDimensions} from 'react-native';
import RenderHtml from 'react-native-render-html';

const html = '<div>Hello world!</div>';

function WebDisplay({html}) {
  const {width: contentWidth} = useWindowDimensions();
  const tagsStyles = {
    a: {
      textDecorationLine: 'none',
    },
  };
  return (
    <RenderHtml
      contentWidth={contentWidth}
      source={{html}}
      tagsStyles={tagsStyles}
    />
  );
}

export default function App() {
  const [isToastVisible, setIsToastVisible] = React.useState(false);
  React.useEffect(function flipToast() {
    const timeout = setTimeout(() => {
      setIsToastVisible((v) => !v);
    }, 30);
    return () => {
      clearTimeout(timeout);
    };
  });
  return (
    <ScrollView contentContainerStyle={styles.container}>
      <WebDisplay html={html} />
      {isToastVisible && <Text>This is a toast!</Text>}
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flexGrow: 1,
  },
});
Run Code Online (Sandbox Code Playgroud)

为什么我会收到此警告,它是什么意思以及如何解决?

Jul*_*lph 39

通常,此警告会在以下情况下出现:

  • 父组件(当前App)更新非常频繁,并导致WebDisplay组件重新渲染。在提供的代码片段中,每 30 毫秒一次;
  • RenderHTML在每次重新渲染之间,至少有一个传递给的 prop 引用不稳定。在提供的代码片段中,tagsStyles每次重新渲染时引用都会发生变化。

App请注意,在钩子引起的组件的每次更新之间useEffecthtml传递给的 propWebDisplay是不变的。但WebDisplay无论如何都会重新渲染,因为它不是“纯粹的”。

出于这个原因,一个非常简单的解决方案是WebDisplay包装React.memo

const WebDisplay = React.memo(function WebDisplay({html}) {
  const {width: contentWidth} = useWindowDimensions();
  const tagsStyles = {
    a: {
      textDecorationLine: 'none',
    },
  };
  return (
    <RenderHtml
      contentWidth={contentWidth}
      source={{html}}
      tagsStyles={tagsStyles}
    />
  );
});
Run Code Online (Sandbox Code Playgroud)

您可以在官方文档中了解有关此技术的更多信息。

注意:“纯”术语来自 React PureComponent类。我认为React.pure不会那么含糊,React.memo因为我们现在使用“记忆化”来指定应用于组件和道具的优化技术,这可能会令人困惑。我更喜欢保留组件的“纯粹”术语和道具的“记忆”。

另一种解决方案是移到函数体tagsStyles之外WebDisplay。在这种情况下,它只会被实例化一次并变得引用稳定。因为RenderHtml它本身是纯粹的,所以它不会重新渲染自己的子组件,并且警告应该消失:

const tagsStyles = {
  a: {
    textDecorationLine: 'none',
  },
};

function WebDisplay({html}) {
  const {width: contentWidth} = useWindowDimensions();
  return (
    <RenderHtml
      contentWidth={contentWidth}
      source={{html}}
      tagsStyles={tagsStyles}
    />
  );
};
Run Code Online (Sandbox Code Playgroud)

注意:将任何不依赖于功能组件主体之外的任何其他状态或道具的道具移动是一个很好的习惯。

最后,如果您的用例涉及tagsStyles依赖于某个 prop,您可以使用以下命令来记住它React.useMemo

function WebDisplay({html, anchorColor}) {
  const {width: contentWidth} = useWindowDimensions();
  const tagsStyles = React.useMemo(
    () => ({
      a: {
        color: anchorColor,
        textDecorationLine: 'none',
      },
    }),
    [anchorColor],
  );
  return (
    <RenderHtml
      contentWidth={contentWidth}
      source={{html}}
      tagsStyles={tagsStyles}
    />
  );
}
Run Code Online (Sandbox Code Playgroud)

有关此钩子的更多信息,请参见官方文档

如果您仍然对 React 中的组件更新如何工作没有清晰的思维模型,我建议您阅读以下内容来增强您的技能: