【问题标题】:"setState(…): Can only update a mounted or mounting component" inside componentDidMountcomponentDidMount 内的“setState(...):只能更新已安装或正在安装的组件”
【发布时间】:2017-05-29 07:21:03
【问题描述】:

相关问题here,但我不确定如何调整该问题的解决方案。

我正在尝试为带有标签的登录页面创建一个可重用的组件。每个选项卡都是可重用组件的子组件,并且有自己的 store 定义为 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 函数中,我可以假设组件已安装。当我刷新页面时,此警告消息就会消失。

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

【问题讨论】:

  • 您链接到的相关问题的接受答案似乎直接适用于您的问题。 (自动绑定在这里并不真正相关——用户似乎也在使用React.createClass,并且描述的问题是事件处理程序没有得到un绑定,因此@987654325 @在componentWillUnmount。)
  • @Jordan:我已经从问题陈述中删除了自动绑定。我仍然不确定如何使用相关问题中的想法。你能详细说明答案吗?

标签: javascript reactjs redux flux arrow-functions


【解决方案1】:

这个函数...

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

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

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

现在,当组件没有挂载时,应该调用() =&gt; {},而不是调用setState,因此是无害的

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • +1 顺便说一句,你可以把var self = this的第二个参数改成箭头函数,去掉那些var self = this
    猜你喜欢
    • 2017-04-26
    • 2016-10-13
    • 1970-01-01
    • 1970-01-01
    • 2017-03-02
    • 2019-08-15
    • 2016-09-25
    • 2018-03-26
    • 2017-05-18
    相关资源
    最近更新 更多