繁体   English   中英

componentDidUpdate() 内部的 setState()

[英]setState() inside of componentDidUpdate()

我正在编写一个脚本,根据下拉菜单的高度和输入在屏幕上的位置,将下拉菜单移动到输入的下方或上方。 我还想根据其方向将修饰符设置为下拉列表。 但是在componentDidUpdate内部使用setState会创建一个无限循环(这是显而易见的)

我找到了使用getDOMNode并将类名直接设置为下拉列表的解决方案,但我觉得应该有使用 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>
        );
    }
});

您可以在componentDidUpdate使用setState 问题在于,您以某种方式创建了一个无限循环,因为没有中断条件。

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

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

例子:

componentDidUpdate(previousProps, previousState) {
    if (previousProps.data !== this.props.data) {
        this.setState({/*....*/})
    }
}

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

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

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

这个例子将帮助你理解React Life Cycle Hooks

您可以在getDerivedStateFromProps方法(即static setState并在componentDidUpdate道具更改后触发该方法。

componentDidUpdate您将获得从getSnapshotBeforeUpdate返回的第三个参数。

您可以检查此代码和框链接

 // Child component class Child extends React.Component { // First thing called when component loaded constructor(props) { console.log("constructor"); super(props); this.state = { value: this.props.value, color: "green" }; } // static method // dont have access of 'this' // return object will update the state static getDerivedStateFromProps(props, state) { console.log("getDerivedStateFromProps"); return { value: props.value, color: props.value % 2 === 0 ? "green" : "red" }; } // skip render if return false shouldComponentUpdate(nextProps, nextState) { console.log("shouldComponentUpdate"); // return nextState.color !== this.state.color; return true; } // In between before real DOM updates (pre-commit) // has access of 'this' // return object will be captured in componentDidUpdate getSnapshotBeforeUpdate(prevProps, prevState) { console.log("getSnapshotBeforeUpdate"); return { oldValue: prevState.value }; } // Calls after component updated // has access of previous state and props with snapshot // Can call methods here // setState inside this will cause infinite loop componentDidUpdate(prevProps, prevState, snapshot) { console.log("componentDidUpdate: ", prevProps, prevState, snapshot); } static getDerivedStateFromError(error) { console.log("getDerivedStateFromError"); return { hasError: true }; } componentDidCatch(error, info) { console.log("componentDidCatch: ", error, info); } // After component mount // Good place to start AJAX call and initial state componentDidMount() { console.log("componentDidMount"); this.makeAjaxCall(); } makeAjaxCall() { console.log("makeAjaxCall"); } onClick() { console.log("state: ", this.state); } render() { return ( <div style={{ border: "1px solid red", padding: "0px 10px 10px 10px" }}> <p style={{ color: this.state.color }}>Color: {this.state.color}</p> <button onClick={() => this.onClick()}>{this.props.value}</button> </div> ); } } // Parent component class Parent extends React.Component { constructor(props) { super(props); this.state = { value: 1 }; this.tick = () => { this.setState({ date: new Date(), value: this.state.value + 1 }); }; } componentDidMount() { setTimeout(this.tick, 2000); } render() { return ( <div style={{ border: "1px solid blue", padding: "0px 10px 10px 10px" }}> <p>Parent</p> <Child value={this.state.value} /> </div> ); } } function App() { return ( <React.Fragment> <Parent /> </React.Fragment> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

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

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

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

您可以在 componentDidUpdate 中使用setState

当循环中没有中断条件时,this.setState 在 ComponentDidUpdate 中使用时会创建一个无限循环。 您可以使用 redux 在 if 语句中设置一个变量 true ,然后在条件中设置变量 false 然后它会起作用。

像这样的东西。

if(this.props.route.params.resetFields){

        this.props.route.params.resetFields = false;
        this.setState({broadcastMembersCount: 0,isLinkAttached: false,attachedAffiliatedLink:false,affilatedText: 'add your affiliate link'});
        this.resetSelectedContactAndGroups();
        this.hideNext = false;
        this.initialValue_1 = 140;
        this.initialValue_2 = 140;
        this.height = 20
    }

我遇到了类似的问题。 请将 componentDidUpdate 设为箭头函数。 那应该工作。

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

我有一个类似的问题,我必须将工具提示居中。 在 componentDidUpdate 中反应 setState 确实让我陷入无限循环,我尝试了它工作的条件。 但是我发现在 ref 回调中使用给我提供了更简单和干净的解决方案,如果您使用内联函数进行 ref 回调,您将面临每个组件更新的空问题。 所以在 ref 回调中使用函数引用并在那里设置状态,这将启动重新渲染

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM