是否有系统的方法来调试导致组件在React中重新呈现的原因?我放了一个简单的console.log()来查看它呈现了多少时间,但我很难弄清楚是什么原因导致组件呈现多次,即(4次)在我的情况下。是否有一个工具可以显示时间轴和/或所有组件树的渲染和顺序?


当前回答

感谢https://stackoverflow.com/a/51082563/2391795的回答,我已经提出了这个稍微不同的解决方案仅功能组件(TypeScript),它也处理状态,而不仅仅是道具。

import {
  useEffect,
  useRef,
} from 'react';

/**
 * Helps tracking the props changes made in a react functional component.
 *
 * Prints the name of the properties/states variables causing a render (or re-render).
 * For debugging purposes only.
 *
 * @usage You can simply track the props of the components like this:
 *  useRenderingTrace('MyComponent', props);
 *
 * @usage You can also track additional state like this:
 *  const [someState] = useState(null);
 *  useRenderingTrace('MyComponent', { ...props, someState });
 *
 * @param componentName Name of the component to display
 * @param propsAndStates
 * @param level
 *
 * @see https://stackoverflow.com/a/51082563/2391795
 */
const useRenderingTrace = (componentName: string, propsAndStates: any, level: 'debug' | 'info' | 'log' = 'debug') => {
  const prev = useRef(propsAndStates);

  useEffect(() => {
    const changedProps: { [key: string]: { old: any, new: any } } = Object.entries(propsAndStates).reduce((property: any, [key, value]: [string, any]) => {
      if (prev.current[key] !== value) {
        property[key] = {
          old: prev.current[key],
          new: value,
        };
      }
      return property;
    }, {});

    if (Object.keys(changedProps).length > 0) {
      console[level](`[${componentName}] Changed props:`, changedProps);
    }

    prev.current = propsAndStates;
  });
};

export default useRenderingTrace;

注意,实现本身并没有太大变化。文档展示了如何在道具/状态和组件中使用它,现在该组件是用TypeScript编写的。

其他回答

你可以使用React Devtools profiler工具检查组件(重新)渲染的原因。不需要更改代码。请参阅react团队的博客文章介绍react分析器。

首先,转到设置cog > profiler,并选择“记录每个组件渲染的原因”

感谢https://stackoverflow.com/a/51082563/2391795的回答,我已经提出了这个稍微不同的解决方案仅功能组件(TypeScript),它也处理状态,而不仅仅是道具。

import {
  useEffect,
  useRef,
} from 'react';

/**
 * Helps tracking the props changes made in a react functional component.
 *
 * Prints the name of the properties/states variables causing a render (or re-render).
 * For debugging purposes only.
 *
 * @usage You can simply track the props of the components like this:
 *  useRenderingTrace('MyComponent', props);
 *
 * @usage You can also track additional state like this:
 *  const [someState] = useState(null);
 *  useRenderingTrace('MyComponent', { ...props, someState });
 *
 * @param componentName Name of the component to display
 * @param propsAndStates
 * @param level
 *
 * @see https://stackoverflow.com/a/51082563/2391795
 */
const useRenderingTrace = (componentName: string, propsAndStates: any, level: 'debug' | 'info' | 'log' = 'debug') => {
  const prev = useRef(propsAndStates);

  useEffect(() => {
    const changedProps: { [key: string]: { old: any, new: any } } = Object.entries(propsAndStates).reduce((property: any, [key, value]: [string, any]) => {
      if (prev.current[key] !== value) {
        property[key] = {
          old: prev.current[key],
          new: value,
        };
      }
      return property;
    }, {});

    if (Object.keys(changedProps).length > 0) {
      console[level](`[${componentName}] Changed props:`, changedProps);
    }

    prev.current = propsAndStates;
  });
};

export default useRenderingTrace;

注意,实现本身并没有太大变化。文档展示了如何在道具/状态和组件中使用它,现在该组件是用TypeScript编写的。

奇怪的是没有人给出这个答案,但我发现它非常有用,特别是因为道具的变化几乎总是深嵌套的。

钩子粉丝:

import deep_diff from "deep-diff";
const withPropsChecker = WrappedComponent => {
  return props => {
    const prevProps = useRef(props);
    useEffect(() => {
      const diff = deep_diff.diff(prevProps.current, props);
      if (diff) {
        console.log(diff);
      }
      prevProps.current = props;
    });
    return <WrappedComponent {...props} />;
  };
};

“老”平衡粉丝:

import deep_diff from "deep-diff";
componentDidUpdate(prevProps, prevState) {
      const diff = deep_diff.diff(prevProps, this.props);
      if (diff) {
        console.log(diff);
      }
}

附注:我仍然更喜欢使用HOC(高阶组件),因为有时你在顶部解构了你的道具,Jacob的解决方案不太适合

免责声明:与包所有者没有任何联系。只是点击数十次,试图找出深嵌套对象之间的差异,这是一种痛苦。

上面的答案非常有用,如果有人正在寻找一种特定的方法来检测重新渲染的原因,那么我发现这个库redux-logger非常有用。

你能做的就是添加库并启用状态差异(它在文档中),如下所示:

const logger = createLogger({
    diff: true,
});

并在存储中添加中间件。

然后在要测试的组件的渲染函数中放入console.log()。

然后,您可以运行应用程序并检查控制台日志。无论在哪里有一个日志之前,它都会显示状态(nextProps和this.props)之间的差异,你可以决定是否真的需要渲染

它将类似于上面的图像连同diff键。

使用钩子和功能组件,而不仅仅是道具的改变会导致渲染。我开始使用的是一个相当手动的日志。这对我帮助很大。你可能也会发现它很有用。

我将这部分复制到组件的文件中:

const keys = {};
const checkDep = (map, key, ref, extra) => {
  if (keys[key] === undefined) {
    keys[key] = {key: key};
    return;
  }
  const stored = map.current.get(keys[key]);

  if (stored === undefined) {
    map.current.set(keys[key], ref);
  } else if (ref !== stored) {
    console.log(
      'Ref ' + keys[key].key + ' changed',
      extra ?? '',
      JSON.stringify({stored}).substring(0, 45),
      JSON.stringify({now: ref}).substring(0, 45),
    );
    map.current.set(keys[key], ref);
  }
};

在方法的开头,我保留了一个WeakMap引用:

const refs = useRef(new WeakMap());

然后在每个“可疑的”电话(道具,钩子)之后,我写道:

const example = useExampleHook();
checkDep(refs, 'example ', example);