" setState(...):只能更新已安装或安装的组件"在componentDidMount

时间:2017-01-13 20:14:27

标签: javascript reactjs redux flux arrow-functions

相关问题here但我不确定如何使解决方案适应此问题。

我正在尝试使用标签为目标网页创建可重用的组件。每个选项卡都是可重用组件的子项,并且将自己的存储定义为prop:

<LandingPage>
    <LandingPage.Tab store={store1}/>
    <LandingPage.Tab store={store2}/>
    ...
    <LandingPage.Tab store={storeN}/>
</LandingPage>

当父组件安装时,我想从每个标签的商店中获取数据,以便在标签之间快速切换。在componentDidMount函数中,我迭代每个子节点并将子存储的onChange回调分配给匿名箭头函数:

var LandingPage = React.createClass({
    getInitialState: function () {
        return {
            data: [] /* each index will be an array of data for a different tab */
        };
    },
    componentDidMount: function () {
        var self = this;
        React.Children.forEach(this.props.children, function (child, index) {
            child.props.store.onChange(() => {
                self.setDataAtIndex(index, child.props.store.getData());
            });
        });
    },
    setDataAtIndex: function (index, newData) {
        var data = this.state.data.slice();
        data[index] = newData;
        this.setState({
            data: data
        });
    },
    ...
});

但是,当页面首次加载时,我收到来自React的警告消息:

  

警告:setState(...):只能更新已安装或安装的组件。这通常意味着您在已卸载的组件上调用了setState()。这是一个无操作。请检查LandingPage组件的代码。

我很困惑因为我认为如果我在componentDidMount函数中,我可以假设组件已挂载。刷新页面时,此警告消息消失。

有人可以解释这种行为并告诉我如何正确构建代码以消除警告信息吗?

2 个答案:

答案 0 :(得分:1)

这个功能......

() => { // this calls setState
    self.setDataAtIndex(index, child.props.store.getData());
}

每次选项卡存储更改是否安装LandingPage组件时都会调用。这是一个问题。当LandingPage卸载时,您需要告诉商店停止调用此函数。在不修改商店的情况下,您可以使用no-op覆盖更改侦听器,例如......

componentWillUnmount: function () {
    var self = this;
    React.Children.forEach(this.props.children, function (child, index) {
        child.props.store.onChange(() => {});
    });
}

现在,当组件未安装时,应调用() => {},而不会调用setState,因此无害

答案 1 :(得分:1)

虽然Charlie Martin的答案是一个聪明的解决方法,但我最终决定将回调存储在一个状态变量中,类似于我链接到的相关问题。这是更新后的代码:

var LandingPage = React.createClass({
    getInitialState: function () {
        return {
            data: [] /* each index will be an array of data for a different tab */
            callbacks: [] /* each index will store a callback reference */
        };
    },
    componentDidMount: function () {
        var self = this;
        React.Children.forEach(this.props.children, function (child, index) {
            var fn = function() {
                self.setDataAtIndex(index, child.props.store.getAll());
            };
            child.props.store.onChange(fn);
            self.saveCallback(index, fn);
        });
    },
    componentWillUnmount: function () {
        var self = this;
        React.Children.forEach(this.props.children, function (child, index) {
            child.props.store.offChange(self.state.callbacks[index]);
        });
    },
    saveCallback: function (index, fn) {
        var callbacks = this.state.callbacks;
        callbacks[index] = fn;
        this.setState({
            callbacks: callbacks
        });
    },
    ...
});

作为参考,onChange()和offChange()定义为:

function onChange(callback) {
    this.on('change', callback);
}
function offChange(callback) {
    this.removeListener('change', callback);
}