当使用 ComponentDidMount() 我发现这个错误:不能调用 setState

IT技术 javascript reactjs redux
2021-04-21 07:12:03

我发现了这个错误:

无法在未安装的组件上调用 setState(或 forceUpdate)。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,请取消 componentWillUnmount 方法中的所有订阅和异步任务。

上下文:当我连接时,我在主页上,这个页面不包含面包屑,但是如果我继续CampaignPage(也是组件的名称),我有BreadCrumb(组件名称)我发现了这个错误。

在我能看到的其他帖子中,他们说异步上可能有问题,ComponentWillMount但我认为我的问题不同,我找不到解决方案。

我的代码看起来像这样:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import classnames from 'classnames';
import objectAssign from 'object-assign';
import { withRouter } from 'react-router';
import {
  BREADCRUMBS_ROUTES,
  BREADCRUMBS_ROUTES_FOR_ID,
  BREADCRUMBS_ENDPOINT
} from 'constants';
import { getEntityById, setUpdatedBreadcrumbs } from 'actions/breadcrumbs';

import style from './style.scss';

class Breadcrumbs extends Component {
  constructor(props) {
    super(props);

    this.state = {
      breadcrumbs: [],
      names: {}
    };

    this.setBreadcrumbs = this.setBreadcrumbs.bind(this);
    this.loadEntityNameById = this.loadEntityNameById.bind(this);
  }
  componentWillMount() {
    this.setBreadcrumbs();
  }

  componentWillReceiveProps(nextProps) {
    const { isWillUpdate: newIsWillUpdate } = nextProps;
    const { isWillUpdate, saveUpdatedBreadcrumbs } = this.props;

    if (isWillUpdate === false && newIsWillUpdate === true) {
      this.setBreadcrumbs();
      saveUpdatedBreadcrumbs();
    }
  }


  setBreadcrumbs() {
    const { params, path } = this.props.match;
    let currentPath = '';

    const pathSplitedAndExtendet = path.split('/')
      .filter(item => !!item)
      .map(item => {
        if (item[0] === ':' && item.slice(1) !== 'adPage') {
          const parameterName = item.slice(1);
          this.loadEntityNameById(
            parameterName,
            params[parameterName]
          );

          return {
            route: `/${params[parameterName]}${BREADCRUMBS_ROUTES_FOR_ID[parameterName]}`,
            parameter: parameterName
          };
        }
        return {
          route: `/${item}`,
          parameter: ''
        };
      });

    const breadcrumbs = pathSplitedAndExtendet
      .filter(item => item.parameter !== 'adPage')
      .map((item) => {
        const indexOfRoute = currentPath.indexOf(item.route);
        if (currentPath.slice(indexOfRoute) !== item.route) {
          currentPath = `${currentPath}${item.route}`;
        }

        return ({
          ...item,
          name: BREADCRUMBS_ROUTES[item.route] || '',
          route: currentPath
        });
      });
    this.setState({ breadcrumbs });
  }

  async loadEntityNameById(parameter, id) {
    const { loadEntityById } = this.props;
    await loadEntityById(BREADCRUMBS_ENDPOINT[parameter], id)
      .then((data) => {
        this.setState({ names: objectAssign(this.state.names, { [parameter]: { id, name: data.name } }) });
      });
  }

  render() {
    const { breadcrumbs, names } = this.state;
    const { showBreadcrumbs } = this.props;
    return (
      <div className={style.breadcrumbs}>
        {
          showBreadcrumbs && breadcrumbs
            .map((item, index) => {
              return (
                <div
                  key={`${item.name}--${item.route}--${index}`}
                  className={classnames(style.bread, index === breadcrumbs.length - 1 ? style.last : null)}
                  role="link"
                  tabIndex={-10 - index}
                  onKeyDown={() => {}}
                  onClick={item.route ? () => this.props.history.push(item.route) : null}
                >
                  {`${item.name || (names[item.parameter]
                    ? names[item.parameter].name : '...')}
                    ${((breadcrumbs.length > 1) && (index !== breadcrumbs.length - 1)) ? ' >' : ''}
                  `}
                </div>
              );
            })
        }
      </div>
    );
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Breadcrumbs));

3个回答

无法在未安装的组件上调用 setState(或 forceUpdate)。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,请取消 componentWillUnmount 方法中的所有订阅和异步任务。

此错误消息明确指出您的应用程序存在内存泄漏。这到底是怎么回事?

好吧,您在方法中使用了异步操作 (loadEntityNameById) setBreadcrumbs哪个被调用 incomponentWillMount和 in componentWillReceiveProps这意味着当您从一个CampaignPage组件转到BreadCrumb另一个组件时,它将执行异步操作,即。loadEntityNameById在后台运行,只有在完成后才设置状态。但在那之前,您的BreadCrumb组件可能会被卸载。React 应用程序不允许您更新未安装组件的状态。

此外,您根本不应该使用componentWillMount方法。改用componentDidMount钩子。

要解决这个问题,你可以做的是设置一个标志,如:

componentDidMount() {
  // component is mounted, set the didMount property on BreadCrumb component
  // you can use any name instead of didMount what you think is proper
  this.didMount = true
  // now, you can update the state
  if(this.didMount) { // be sure it's not unmounted
    this.setState({names: ...})
  }

接下来,您应该在卸载组件时清除 didMount 属性。

componentWillUnmount() {
  this.didMount = false // component is unmounted

这将确保您的应用程序内存不会泄漏。因为,我们在需要时正确设置状态,而不是在不需要时正确设置状态,并停止不必要的循环。

您可以使用任何一种方式。
2021-05-23 07:12:03
谢谢你我明天在办公室试试!
2021-06-05 07:12:03
如果您需要任何帮助,请告诉我。
2021-06-15 07:12:03
我如何将我的Promise从我的异步/等待操作返回到componentDidMountfor using setState 中值使用。或者可能将所有功能移动到 componentDidMount 中?
2021-06-16 07:12:03
好的,我找到了解决方案。我做了一个全局变量,如this.didMount设置为真,正如你告诉我的,在我this.setState检查是否this.didMount等于真之前,在我的 async/await 函数中,如果为真,我可以使用,this.setState否则什么都不会发生。下次我会注意的。
2021-06-22 07:12:03

您正在执行一个异步操作 ( loadEntityNameById),它在完成时设置组件的状态。到那时,您的Breadcrumbs组件可能已卸载,并抛出错误。

你可以给我建议吗?如果我卸载了loadEntityNameById
2021-06-16 07:12:03
希望这篇文章可以解释一些策略reactjs.org/blog/2015/12/16/ismounted-antipattern.html
2021-06-20 07:12:03

你不能调用setStatecomponentWillMount尝试使用componentDidMount替代

好的,谢谢,但这是一个小问题,当我们找到解决此问题的方法后,我解决了这个问题
2021-05-28 07:12:03