SetState()位于组件 DidUpdate()内部

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

我发现了一个解决方案,使用 getDOMNode和设置 classname 直接下拉,但我觉得应该有一个更好的解决方案使用反应工具。有人能帮我吗?

下面是使用 getDOMNode(i 为了简化代码而忽略的定位逻辑)

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>
);
}
});
195380 次浏览

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

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

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

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

如果只是通过向组件发送道具来更新组件(setState 没有更新组件,组件 DidUpdate 中的情况除外) ,那么可以在 componentWillReceiveProps中调用 setState,而不是在 componentDidUpdate中。

我有一个类似的问题,我必须中心的工具提示。组件中的 React setState DidUpdate 确实让我陷入了无限循环,我试着让它工作。但是我发现使用 ref 回调给了我更简单和干净的解决方案,如果使用内联函数进行 ref 回调,那么每次组件更新都会面临 null 问题。因此,在 ref 回调中使用函数引用并在那里设置状态,这将启动重新呈现

我想说的是,您需要检查状态是否已经具有您试图设置的相同值。如果是相同的,就没有必要为相同的值重新设置状态。

确保你的状态是这样的:

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

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

例如:

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

这个例子将帮助您理解 反应生命周期挂钩

你可以 getDerivedStateFromProps方法中的 setState,也就是 static,在 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>

您可以在组件 DidUpdate 中使用 SetState

SetState 在 Component DidUpdate 中使用时,如果循环中没有中断条件,则创建一个无限循环。 您可以使用 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
}