跟踪 React 组件重新渲染的原因

IT技术 reactjs redux
2021-04-11 03:43:01

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

6个回答

如果你想要一个没有任何外部依赖的简短片段,我觉得这很有用

componentDidUpdate(prevProps, prevState) {
  Object.entries(this.props).forEach(([key, val]) =>
    prevProps[key] !== val && console.log(`Prop '${key}' changed`)
  );
  if (this.state) {
    Object.entries(this.state).forEach(([key, val]) =>
      prevState[key] !== val && console.log(`State '${key}' changed`)
    );
  }
}

这是我用来跟踪功能组件更新的一个小钩子

function useTraceUpdate(props) {
  const prev = useRef(props);
  useEffect(() => {
    const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
      if (prev.current[k] !== v) {
        ps[k] = [prev.current[k], v];
      }
      return ps;
    }, {});
    if (Object.keys(changedProps).length > 0) {
      console.log('Changed props:', changedProps);
    }
    prev.current = props;
  });
}

// Usage
function MyComponent(props) {
  useTraceUpdate(props);
  return <div>{props.children}</div>;
}
在函数组件中,你可以像这样使用它function MyComponent(props) { useTraceUpdate(props); },它会在 props 改变时记录
2021-05-27 03:43:01
@DawsonB 您可能在该组件中没有任何状态,因此this.state未定义。
2021-05-30 03:43:01
除此之外,如果您发现某个状态正在更新并且不清楚在哪里或为什么,您可以覆盖该setState方法(在类组件中),setState(...args) { super.setState(...args) }然后在您的调试器中设置一个断点,然后您就可以追溯到设置状态的函数。
2021-06-07 03:43:01
@yarden.refaeli 我认为没有理由使用 if 块。简短而简洁。
2021-06-08 03:43:01
我究竟如何使用钩子函数?在我useTraceUpdate按照你写的那样定义之后,我到底应该在哪里打电话
2021-06-11 03:43:01

以下是 React 组件将重新渲染的一些实例。

  • 父组件重新渲染
  • this.setState()在组件内调用这将触发以下组件生命周期方法shouldComponentUpdate> componentWillUpdate> render>componentDidUpdate
  • 组件的props. 这将触发componentWillReceiveProps> shouldComponentUpdate> componentWillUpdate> render> componentDidUpdate当 Redux 存储中有适用的更改时触发此connect方法react-redux
  • 调用this.forceUpdate类似于this.setState

您可以通过在内部实现检查shouldComponentUpdatefalse在不需要时返回来最小化组件的重新渲染

另一种方法是使用React.PureComponent 或无状态组件。纯组件和无状态组件仅在其 props 发生更改时重新渲染。

通过useContext-hook 而不是<SomeContext.Consumer>....
2021-05-23 03:43:01
这肯定是一个很好的答案,但它没有回答真正的问题, - 如何追踪触发重新渲染的原因。雅各布 R 的答案在给出实际问题的答案方面看起来很有希望。
2021-06-01 03:43:01
挑剔:“无状态”只是指任何不使用状态的组件,无论它是用类语法还是函数语法定义的。此外,功能组件总是重新渲染。您需要使用shouldComponentUpdate或扩展React.PureComponent来强制仅在更改时重新呈现。
2021-06-02 03:43:01
因此,即使您使用创建组件的功能方式,例如const MyComponent = (props) => <h1>Hello {props.name}</h1>;(这是一个无状态组件)。只要父组件重新渲染,它就会重新渲染。
2021-06-08 03:43:01
您对无状态/功能组件总是重新渲染是正确的。将更新我的答案。
2021-06-10 03:43:01

您可以使用 React Devtools 分析器工具检查组件(重新)渲染的原因。无需更改代码。请参阅React团队的博文Introducing the React Profiler

首先,转到设置 cog > profiler,然后选择“记录每个组件呈现的原因”

React 开发工具 > 设置

React Devtools 分析器的屏幕截图

2021-06-11 03:43:01

@jpdelatorre 的回答非常适合强调 React 组件可能重新渲染的一般原因。

我只是想更深入地研究一个实例:when props change对导致 React 组件重新渲染的原因进行故障排除是一个常见问题,根据我的经验,很多时候追踪这个问题涉及确定哪些props正在改变

React 组件在收到新的 props 时会重新渲染。他们可以获得新props,例如:

<MyComponent prop1={currentPosition} prop2={myVariable} />

或者如果MyComponent连接到 redux 存储:

function mapStateToProps (state) {
  return {
    prop3: state.data.get('savedName'),
    prop4: state.data.get('userCount')
  }
}

任何时候的valueprop1prop2prop3,或prop4改变MyComponent将重新呈现。使用 4 个props,通过console.log(this.props)render块的开头放置一个来追踪哪些props正在改变并不难但是随着组件越来越复杂,props越来越多,这种方法是站不住脚的。

这是一个有用的方法(为了方便使用lodash)来确定哪些 prop 更改导致组件重新渲染:

componentWillReceiveProps (nextProps) {
  const changedProps = _.reduce(this.props, function (result, value, key) {
    return _.isEqual(value, nextProps[key])
      ? result
      : result.concat(key)
  }, [])
  console.log('changedProps: ', changedProps)
}

将此代码片段添加到您的组件中可以帮助揭示导致可疑重新渲染的罪魁祸首,而且很多时候这有助于阐明通过管道传输到组件中的不必要数据。

它现在被调用UNSAFE_componentWillReceiveProps(nextProps)并且已被弃用。“这个生命周期以前被命名为componentWillReceiveProps。这个名字将继续有效,直到版本 17。” 来自React 文档
2021-06-10 03:43:01
您可以使用 componentDidUpdate 实现相同的效果,无论如何它可以说更好,因为您只想找出导致组件实际更新的原因。
2021-06-17 03:43:01

奇怪的是没有人给出这个答案,但我发现它非常有用,尤其是因为props更改几乎总是深度嵌套的。

勾搭粉丝:

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

PS 我仍然更喜欢使用 HOC(高阶组件),因为有时你在顶部解构了你的props,而雅各布的解决方案并不适合

免责声明:与包所有者没有任何关系。只需单击数十次以尝试发现深层嵌套对象中的差异是一种痛苦。

为了节省其他人的一些谷歌搜索: npm deep-diffgithub 上的 deep-diff 源(源链接是 npm 页面上的“存储库”链接。)
2021-05-22 03:43:01