当前位置: 首页 > 面试题库 >

当使用ComponentDidMount()时,我发现此错误:无法调用setState

沈乐邦
2023-03-14
问题内容

我发现此错误:

无法在已卸载的组件上调用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方法中取消所有订阅和异步任务。

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

好吧,您在方法中使用了异步操作(loadEntityNameById)setBreadcrumbs。正在componentWillMount和中调用哪个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

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



 类似资料: