ReactJS - 卸载和安装时设置状态错误

ReactJS - setState error when unMounting and Mounting

我是 React 新手,所以我可能不理解正确的思维概念。

我想做一个可以挂载和卸载的自动更新数据组件。

这是我在卸载然后安装组件时遇到的错误:

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the MeasurementsDataTable component.

这是代码:

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

我认为问题出在您的 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()

这一切都发生在组件安装之前,因为这一切都发生在componentWillMountWill是这里的关键词。换句话说,正是错误消息所说的内容。

This usually means you called setState() on an unmounted component.

你应该在 componentDidMount 中添加任何可能改变组件状态的东西,这发生在组件安装之后,以确保它不会陷入循环或尝试更新一个未安装的组件。

请记住,渲染组件两次完全没问题。第一次,你挂载它并渲染类似 Loading... 的东西。然后在 componentDidMount 中,你做任何 api 获取,添加事件监听器和其他需要做的事情。最后,您使用实际内容更新 (setState()) 组件。

您必须确保您的组件没有仍在侦听附加的事件侦听器并在卸载后尝试更新自身。

您需要在使用 React 时跟踪所有事件侦听器。如果来自事件侦听器的数据导致您的组件更新,您必须将侦听器附加到生命周期方法 componentWillMount() 并在 componentWillUnmount().

中分离所有侦听器

侦听器可以是您的组件侦听并通过 setState() 方法自行更改的任何内容。它可以是来自套接字的数据、来自服务器的数据、window 调整大小事件、setInterval 侦听器等

实际上,您正在 componentDidMount 中设置间隔,这是在幕后进行的,并试图设置已卸载的组件的状态。

那么看看你的间隔是否成功清除了? 在你的 componentWillUnmount 中还有一件事尝试删除所有具有特定 id 的侦听器或清除所有,

现在你没有清除你没有传递任何 id 所以我认为问题出在听众身上。