ReactJS: «Невозможно выполнить обновление состояния React для отключенного компонента» при открытии диалога - PullRequest
0 голосов
/ 24 мая 2019

Я получил сообщение об ошибке при открытии диалога из другого компонента класса: «Невозможно выполнить обновление состояния 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.

Когда я открываю страницу в первый раз и нажимаю кнопку Открыть, то все работает нормально Но когда я переключаюсь на другую страницу с помощью Router в меню, а затем снова открываю страницу, я получаю сообщение об ошибке в журнале консоли.

Я пытался использовать this.mounting var для проверки не смонтированного компонента, но я не знаю, как установить состояние для открытия подробного диалогового окна, когда компонент был размонтирован во второй и следующий раз.

Я попытался использовать controller = new AbortController (); и controller.abort () в componentWillUnmount (), но не работает.

Или есть решение этой проблемы?

Спасибо!

Изображение: https://prnt.sc/nsp251

изображение ошибки в журнале консоли

Источник на CodeSandbox: https://codesandbox.io/s/coreuicoreuifreereactadmintemplate-5unwj

Шаг теста:

  • Нажмите Меню объявления (1-е)

  • Нажмите группу объявлений

  • Нажмите Меню объявления (2-е)

  • Нажмите Открыть диалог в меню рекламы

  • Просмотр журнала браузера консоли

Файл: src / views / category

Узел v11.12.0

Npm 6,7.0

Окно 10

Ответы [ 2 ]

0 голосов
/ 24 мая 2019

Ваша проблема заключается в использовании внешней функции showDetailDialog для доступа к состоянию компонента DetailDialog.Функция, используемая в вашем компоненте AdMenu, и функция, связанная с компонентом DetailDialog в его конструкторе, не являются одним и тем же.

Решением будет использование Refs и выставить функцию open на самом компоненте.

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} />
            { ... }
        );
    }
}

Но этот подход не рекомендуется в документации React для ссылок .

ТамВот несколько хороших примеров использования ссылок:

  • Управление фокусировкой, выделением текста или воспроизведением мультимедиа.
  • Запуск императивных анимаций.
  • Интеграция со сторонним DOMбиблиотеки.

Избегайте использования ссылок для всего, что можно сделать декларативно.

Например, вместо предоставления методов open() и close() в компоненте Dialog, передайте isOpen prop to it.

По рекомендации документации вы можете объявить состояние detailOpen для компонента AdMenu и передать его компоненту DetailDialog как open prop.

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

Что подходитВсе, что вы выбираете, зависит от вас.

0 голосов
/ 24 мая 2019

Переместите логику, которую вы написали в componentWillMount в componentDidMount в AdMenu

...