我发现此错误:


  无法在已卸载的组件上调用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));

最佳答案

无法在已卸载的组件上调用setState(或forceUpdate)。这是空操作,但它表明应用程序中发生内存泄漏。要解决此问题,请在componentWillUnmount方法中取消所有订阅和异步任务。


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

好吧,您正在setBreadcrumbs方法中使用异步操作(loadEntityNameById)。在componentWillMountcomponentWillReceiveProps中调用哪个。这意味着当您从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


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

10-06 07:49