ReactJS - ошибка setState при размонтировании и монтировании

Я новичок в React, поэтому могу не понимать правильную концепцию мышления.

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

Это ошибка, которую я получаю, когда размонтировал, а затем смонтировал компонент:

Предупреждение: setState (...): может обновлять только смонтированный или монтируемый компонент. Обычно это означает, что вы вызвали setState () на размонтированном компоненте. Это запретная игра. Пожалуйста, проверьте код для компонента MeasurementsDataTable.

а вот код:

var getDataInretval;
var listenersService = new ListenersService();
var Data = [{}];

function ListenersService(){
    var listeners = {};
    this.addListener = function(callback){
        var id;
        if(typeof callback === 'function'){
            id = Math.random().toString(36).slice(2);
            listeners[id] = callback;
        }
        return id;
    }
    this.removeListener = function( id){
        if(listeners[id]){
            delete listeners[id];
            return true;
        }
        return false;
    }
    this.notifyListeners = function(data){
        for (var id in listeners) {
            if(listeners.hasOwnProperty(id)){
                listeners[id](data);
            }
        }
    }
}


var dataSevice = new DataMeasurementService(ListenersService);

function DataMeasurementService(ListenersService){

    Data.push( new MeasurementDataForTable("header1", "th", "Phase measurements", "L1", "L2", "L3", "Total", "Others") );
    var self = this;

    //var listenersService = new ListenersService();
    this.addListener = listenersService.addListener;
    this.removeListener = listenersService.removeListener;
    this.getData = function(){
        return Data;
    }

    $.ajax({
        url: "BL/getMeasurementsData.php",
        type: "GET",
        dataType: "html",
        async: false,
        success: function(res) {
            var parseData = parseMeasurementsData( res );
            Data = createOriginData( parseData );
        },
        error: function(request, status, error) {
            alert("error: " + request.responseText);
        }
    });
    listenersService.notifyListeners(Data);
}

var ThElement = React.createClass({
   render: function(){
       return <th  width={this.props.width}>{this.props.data}</th>;
   }
});

var TdElement = React.createClass({
    render: function(){
        return <td>{this.props.data}</td>;
    }
});

var MeasurementsDataTable = React.createClass({
    getInitialState: function() {
        return {
            data: this.props.dataService.getData()
        };
    },
    componentDidMount: function() {
        getDataInretval = setInterval(function(){
            $.ajax({
                url: "BL/getMeasurementsData.php",
                type: "GET",
                dataType: "html",
                async: false,
                success: function(res) {
                    var parseData = parseMeasurementsData( res );
                    Data = createOriginData( parseData );
                },
                error: function(request, status, error) {
                    alert("error: " + request.responseText);
                }
            });

            listenersService.notifyListeners(Data);
        }, 1000);


    },
    componentWillMount: function () {
        this.props.dataService.addListener(this.updateHandler);
    },
    componentWillUnmount: function () {
        this.removeListener = listenersService.removeListener;
        clearInterval(getDataInretval);
    },
    updateHandler: function(data) {
        this.setState({
            data: data
        });
    },
    render: function() {
        return (
            <div>
                <table>
                    {
                        this.state.data.map(function(item) {
                                if( item.element == "th" ){
                                    return (
                                        <thead><tr>
                                            <ThElement width="280" data={item.description}/>
                                            <ThElement width="150" data={item.L1}/>
                                            <ThElement width="150" data={item.L2}/>
                                            <ThElement width="150" data={item.L3}/>
                                            <ThElement width="150" data={item.total}/>
                                            <ThElement width="150" data={item.others}/>
                                        </tr></thead>
                                    )
                                }
                                else{
                                        return (
                                            <tr>
                                                <TdElement data={item.description}/>
                                                <TdElement data={item.L1}/>
                                                <TdElement data={item.L2}/>
                                                <TdElement data={item.L3}/>
                                                <TdElement data={item.total}/>
                                                <TdElement data={item.others}/>
                                            </tr>
                                        )
                                    }
                        })
                    }
               </table>
            </div>
        );
    }
});
ReactDOM.render( <MeasurementsDataTable dataService={dataSevice} />, document.getElementById("tablePlaceHolder") );


person Jan    schedule 17.12.2015    source источник
comment
Я не эксперт по реагированию, но можете ли вы попробовать переместить addListener с componentWillMount на componentDidMount?   -  person Mike Szyndel    schedule 17.12.2015


Ответы (3)


Вы должны убедиться, что ваш компонент по-прежнему не прослушивает подключенный прослушиватель событий и не пытается обновить себя после размонтирования.

Вам нужно отслеживать ВСЕ прослушиватели событий при использовании React. Если данные от слушателей событий вызывают обновление вашего компонента, вы должны присоединить слушателя к методу жизненного цикла componentWillMount() и отсоединить все ваши слушатели в componentWillUnmount().

Слушателями может быть все, что ваш компонент слушает и изменяет сам setState() методом. Это могут быть данные из сокета, данные с сервера, событие изменения размера окна, прослушиватель setInterval и т. Д.

person Naisheel Verdhan    schedule 17.12.2015
comment
Я добавляю идентификатор слушателю, а затем удаляю его в componentWillMount. Оно работает. Спасибо. - person Jan; 18.12.2015

Я считаю, что проблема в вашем componentWillMount.

Перед установкой компонента выполните следующие действия.

Добавить слушателя в componentWillMount

componentWillMount: function () {
    this.props.dataService.addListener(this.updateHandler);
},

Это означает, что вы запускаете эту функцию

DataMeasurementService(ListenersService)

В самом конце выполнения этой функции вы запускаете

listenersService.notifyListeners(Data);

Что, в свою очередь, запускает все добавленные вами слушатели, включая только что добавленный this.updateHandler, который выглядит следующим образом

updateHandler: function(data) {
    this.setState({
        data: data
    });
},

И будет setState().

Все это происходит до монтирования компонента, поскольку все это происходит в componentWillMount, где Will является ключевым словом. Другими словами, именно то, что написано в сообщении об ошибке.

Обычно это означает, что вы вызвали setState () на размонтированном компоненте.

Вы должны добавить все, что могло бы изменить состояние компонента в componentDidMount, что происходит после того, как компонент смонтирован, чтобы убедиться, что он не застревает в цикле или не пытается обновить отключенный компонент.

Помните, совершенно нормально визуализировать компонент дважды. В первый раз вы монтируете его и рендерете что-то вроде Loading.... Затем в componentDidMount вы выполняете любую выборку api, добавляете прослушиватели событий и другие вещи, которые необходимо сделать. Наконец, вы обновляете (setState()) компонент, добавляя его фактическое содержимое.

person Magnus Engdal    schedule 17.12.2015
comment
в соответствии с реакциями, вы можете установить состояние в componentWillMount - person Mike Szyndel; 17.12.2015

На самом деле вы устанавливаете интервал в componentDidMount, который происходит за сценой, и пытаетесь установить состояние вашего компонента, который отключается.

Так что просто проверьте, успешно ли сброшен ваш интервал? И еще одна вещь в вашем componentWillUnmount: попытаться удалить всех слушателей с определенным идентификатором или очистить все,

Теперь вы не очищаете, вы не передаете никакого идентификатора. Поэтому я думаю, что проблема связана со слушателями.

person Bhuvnesh    schedule 17.12.2015