0

从另一个类组件打开对话框时出现错误:“无法对未安装的组件执行 React 状态更新。这是一个无操作,但它表明您的应用程序中存在内存泄漏。要修复,请取消所有订阅和componentWillUnmount 方法中的异步任务"

index.js

import ...

class AdMenu extends Component {
    componentWillMount = () => {
        this.onSearch();
    };

    onOpenInsert = () => {
        showDetailDialog();
    };

    onSearch = () => {
        fetch(_url, ...)
            .then(response => {
                if (response.ok) {
                    return response.json();
                } else {
                    throw response;
                }
            })
            .then(responseJson => {
                this.setState({...});
            })
            .catch(response => {...});
    };

    render() {
        return (
            <div>
                <DetailDialog />
                <Button color="primary" onClick={this.onOpenInsert}>Add New</Button>
                <BootstrapTable .... />
            </div>
        );
    }
}

export default withTranslation()(AdMenu);

DetailDialog.js


export var showDetailDialog = function () {
    this.setState({open: true});
    console.log('Mounted: ' + this.mounted);
};

class DetailDialog extends React.Component {
    mounted = false;
    controller = new AbortController();
    constructor(props) {
        super(props);
        this.state = {open: false};
        showDetailDialog = showDetailDialog.bind(this);
    }
    componentDidMount() {
        console.log('componentDidMount');
        this.mounted = true;
    }

    componentWillUnmount(){
        console.log('componentWillUnmount');
        this.mounted = false;
    }
    onClose = () => {
        this.setState({open: false});
    };

    render() {
        return (
            <Modal isOpen={this.state.open} toggle={this.onClose} className={"modal-primary"} >
                <ModalHeader toggle={this.onClose}>Detail</ModalHeader>
                <ModalBody>
                    ...
                </ModalBody>
            </Modal>
        );
    }
}

export default withTranslation()(DetailDialog);

我有一个 DetailDialog 导出的类组件和函数 showDetailDialog。它导入到 index.js 页面。

当我第一次打开页面并单击打开对话框时,工作正常。但是当我在菜单中通过路由器切换到另一个页面然后第二次再次打开页面时,控制台日志中出现错误。

我尝试使用 this.mounted var 检查未安装的组件,但我不知道当组件在第二次和下一次卸载时如何设置状态以打开详细信息对话框。

我试过使用 controller = new AbortController(); 和 componentWillUnmount() 中的 controller.abort() 但不起作用。

或者这个问题的任何解决方案?

谢谢!

图片:https ://prnt.sc/nsp251

控制台日志中的错误图像

CodeSandbox 上的来源:https ://codesandbox.io/s/coreuicoreuifreereactadmintemplate-5unwj

阶梯测试:

  • 点击广告菜单(第 1 次)

  • 点击广告组

  • 点击广告菜单(第 2 次)

  • 点击广告菜单中的打开对话框

  • 查看控制台日志浏览器

文件:src/views/category

节点 v11.12.0

Npm 6.7.0

窗口 10

4

2 回答 2

0

您的问题是使用外部函数showDetailDialog来访问DetailDialog组件的状态。在你的AdMenu组件中使用的函数和在其构造函数中绑定到该DetailDialog组件的函数不是一回事。

一个解决方案是使用Refsopen并在组件本身上公开一个函数。

class DetailDialog extends Component {
    open = () => this.setState({ open: true });
}

/* ... */

class AdMenu extends Component {
    constructor(props) {
        super(props);
        this.detailDialog = React.createRef();
        this.onOpenInsert = this.onOpenInsert.bind(this);
    }

    onOpenInsert() {
        this.detailDialog.current.open();
    }

    render() {
        return (
            <DetailDialog ref={this.detailDialog} />
            { ... }
        );
    }
}

但是Refs的 React 文档不推荐这种方法。

refs 有一些很好的用例:

  • 管理焦点、文本选择或媒体播放。
  • 触发命令式动画。
  • 与第三方 DOM 库集成。

避免将 refs 用于任何可以以声明方式完成的事情。

例如,不要在 Dialog 组件上公开open()和方法,而是将prop 传递给它。close()isOpen

根据文档的建议,您可以detailOpen在组件上声明一个状态并将其作为道具AdMenu传递给组件。DetailDialogopen

class AdMenu extends Component {
    constructor(props) {
        super(props);
        this.state = {
            detailOpen: false
        }
    }

    onOpenInsert() {
        this.setState({ detailOpen: true });
    }
    
    onDialogClose() {
        this.setState({ detailOpen: false });
    }
    
    /* ... */

    render() {
        return (
            <DetailDialog open={this.state.detailOpen} onClose={this.onDialogClose} />
            { ... }
        );
    }
}

/* ... */

class DetailDialog extends Component {
    /* ... */

    render() {
        return (
            <Modal isOpen={this.props.open} toggle={this.props.onClose}>
                <ModalHeader toggle={this.props.onClose}>Detail</ModalHeader>
                <ModalBody>
                    ...
                </ModalBody>
            </Modal>
        );
    }
}

您选择哪种方法取决于您。

于 2019-05-24T10:03:47.727 回答
0

将您写入的逻辑移动componentWillMountcomponentDidMountAdMenu

于 2019-05-24T08:36:05.697 回答