![](/img/trans.png)
[英]React setState inside componentDidUpdate causing infinite loop
[英]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.