在componentDidUpdate()内部的setState()

JavaScript

Davaid飞云

2020-03-12

我正在编写一个脚本,该脚本根据下拉列表的高度和输入在屏幕上的位置将下拉列表移动到输入下方或上方。我也想将修改器设置为根据其方向下拉。但是setState在内部使用componentDidUpdate会产生无限循环(这很明显)

我已经找到了使用getDOMNode和直接将classname设置为下拉列表的解决方案,但是我觉得应该使用React工具有更好的解决方案。有谁能够帮助我?

这是工作代码的一部分getDOMNode(略微忽略了定位逻辑,以简化代码)

let SearchDropdown = React.createClass({
    componentDidUpdate(params) {
        let el = this.getDOMNode();
        el.classList.remove('dropDown-top');
        if(needToMoveOnTop(el)) {
            el.top = newTopValue;
            el.right = newRightValue;
            el.classList.add('dropDown-top');
        }
    },
    render() {
        let dataFeed = this.props.dataFeed;
        return (
            <DropDown >
                {dataFeed.map((data, i) => {
                    return (<DropDownRow key={response.symbol} data={data}/>);
                })}
            </DropDown>
        );
    }
});

这是带有setstate的代码(它创建一个无限循环)

let SearchDropdown = React.createClass({
    getInitialState() {
        return {
            top: false
        };
    },
    componentDidUpdate(params) {
        let el = this.getDOMNode();
        if (this.state.top) {
           this.setState({top: false});
        }
        if(needToMoveOnTop(el)) {
            el.top = newTopValue;
            el.right = newRightValue;
            if (!this.state.top) {
              this.setState({top: true});
           }
        }
    },
    render() {
        let dataFeed = this.props.dataFeed;
        let class = cx({'dropDown-top' : this.state.top});
        return (
            <DropDown className={class} >
                {dataFeed.map((data, i) => {
                    return (<DropDownRow key={response.symbol} data={data}/>);
                })}
            </DropDown>
        );
    }
});

第874篇《在componentDidUpdate()内部的setState()》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

5个回答
Mandy猴子 2020.03.12

componentDidUpdate签名void::componentDidUpdate(previousProps, previousState)有了它,您将能够测试哪些道具/状态很脏,并进行相应的调用setState

例:

componentDidUpdate(previousProps, previousState) {
    if (previousProps.data !== this.props.data) {
        this.setState({/*....*/})
    }
}
A小宇宙 2020.03.12

如果在setState内部使用componentDidUpdate它会更新组件,从而导致调用componentDidUpdate,随后setState再次调用该调用会导致无限循环。您应该有条件地致电setState并确保违反电话的条件最终会发生,例如:

componentDidUpdate: function() {
    if (condition) {
        this.setState({..})
    } else {
        //do something else
    }
}

如果您仅通过向其发送道具来更新组件(除了componentDidUpdate内部的情况,setState不会更新它),则可以调用setState内部componentWillReceiveProps而不是componentDidUpdate

Stafan小小斯丁 2020.03.12

我要说的是,您需要检查状态是否已经具有您要设置的相同值。如果相同,则没有必要再次为相同的值设置状态。

确保像这样设置您的状态:

let top = newValue /*true or false*/
if(top !== this.state.top){
    this.setState({top});
}
超威蓝喵 2020.03.12

我有一个类似的问题,我必须居中工具提示。在componentDidUpdate中的React setState确实使我陷入了无限循环,我尝试了条件工作的条件。但是我发现在ref回调中使用给我提供了更简单干净的解决方案,如果将内联函数用于ref回调,则每次组件更新都将面临null问题。因此,请在ref回调中使用函数引用并在其中设置状态,这将启动重新渲染

伽罗L 2020.03.12

你可以setState在里面使用componentDidUpdate问题在于,由于没有中断条件,因此您正在以某种方式创建无限循环。

基于您需要呈现组件后浏览器提供的值的事实,我认为您的使用方法componentDidUpdate是正确的,它只需要更好地处理触发的条件即可setState

问题类别

JavaScript Ckeditor Python Webpack TypeScript Vue.js React.js ExpressJS KoaJS CSS Node.js HTML Django 单元测试 PHP Asp.net jQuery Bootstrap IOS Android