当使用 ComponentDidMount() 我发现这个错误:不能调用 setState [英] When use ComponentDidMount() I found this error : Can't call setState

查看:38
本文介绍了当使用 ComponentDidMount() 我发现这个错误:不能调用 setState的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现了这个错误:

<块引用>

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

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

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

我的代码看起来像这样:

import React, { Component } from 'react';从 'prop-types' 导入 PropTypes;从'react-redux'导入{连接};从类名"导入类名;从'object-assign'导入objectAssign;从反应路由器"导入 { withRouter };进口 {BREADCRUMBS_ROUTES,BREADCRUMBS_ROUTES_FOR_ID,面包屑_ENDPOINT来自'常量';导入 { getEntityById, setUpdatedBreadcrumbs } from 'actions/breadcrumbs';从'./style.scss'导入样式;类面包屑扩展组件{构造函数(道具){超级(道具);this.state = {面包屑: [],名称:{}};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();}}设置面包屑(){const { params, path } = this.props.match;让当前路径 = '';const pathSplitedAndExtendet = path.split('/').filter(item => !!item).map(item => {if (item[0] === ':' && item.slice(1) !== 'adPage') {const 参数名 = item.slice(1);this.loadEntityNameById(参数名称,参数[参数名称]);返回 {路线:`/${params[parameterName]}${BREADCRUMBS_ROUTES_FOR_ID[parameterName]}`,参数:参数名称};}返回 {路线:`/${item}`,范围: ''};});const 面包屑 = pathSplitedAndExtendet.filter(item => item.parameter !== 'adPage').map((项目) => {const indexOfRoute = currentPath.indexOf(item.route);if (currentPath.slice(indexOfRoute) !== item.route) {currentPath = `${currentPath}${item.route}`;}返回 ({...物品,名称:BREADCRUMBS_ROUTES[item.route] ||'',路线:当前路径});});this.setState({面包屑});}异步 loadEntityNameById(参数,id){const { loadEntityById } = this.props;等待 loadEntityById(BREADCRUMBS_ENDPOINT[参数], id).then((数据) => {this.setState({ 名称: objectAssign(this.state.names, { [参数]: { id, name: data.name } }) });});}使成为() {const { 面包屑,名称 } = this.state;const { showBreadcrumbs } = this.props;返回 (<div className={style.breadcrumbs}>{showBreadcrumbs &&面包屑.map((item, index) => {返回 (

{}}onClick={item.route ?() =>this.props.history.push(item.route) : null}>{`${item.name ||(名称[项目.参数]?名称[item.parameter].name : '...')}${((breadcrumbs.length > 1) && (index !== breadcrumbs.length - 1)) ?' >':''}`}

);})}

);}}export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Breadcrumbs));

解决方案

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

此错误消息明确指出您的应用程序存在内存泄漏.这里到底发生了什么?

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

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

要解决此问题,您可以设置如下标志:

componentDidMount() {//组件已挂载,在 BreadCrumb 组件上设置 didMount 属性//你可以使用任何你认为合适的名字来代替 didMountthis.didMount = 真//现在,您可以更新状态if(this.didMount) {//确保它没有被卸载this.setState({names: ...})}

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

componentWillUnmount() {this.didMount = false//组件被卸载

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

I found this error :

Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

Context : When I'm connected, I'm on the homepage, this page not contain the breadCrumb, but If I go on CampaignPage (also the name of the component), I have the BreadCrumb (Component name) I found this error.

On other post what I could see, they said probably problem on asynchronously on ComponentWillMount but I think my problem is different and I can't find a solution.

My code look like that :

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

解决方案

Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

This error message clearly states that your application has memory leak. What's going on here exactly?

Well, you're using async operation (loadEntityNameById) in setBreadcrumbs method. Which is being called in componentWillMount and in componentWillReceiveProps. This means when you go from CampaignPage component to BreadCrumb component, it will do the async operation ie. loadEntityNameById is running in the background which only sets the state once it's finished. But until that time your BreadCrumb component might be unmounted. The react application doesn't allow you to update the state on an unmounted component.

Furthermore, you should not use componentWillMount method at all. Use componentDidMount hook instead.

To fix the issue, what you can do is to set a flag like:

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: ...})
  }

Next, you should clear the didMount property when the component is unmounted.

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

This will ensure you that your application memory will not be leaked. Because, we properly setting the state when required but not when it doesn't require, and stopping unnecessary loop.

这篇关于当使用 ComponentDidMount() 我发现这个错误:不能调用 setState的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆