[javascript] componentDidUpdate () 내부의 setState ()
드롭 다운의 높이와 화면의 입력 위치에 따라 드롭 다운을 입력의 위 또는 아래로 이동시키는 스크립트를 작성 중입니다. 또한 방향에 따라 수정자를 드롭 다운하도록 설정하고 싶습니다. 그러나 setState
내부를 사용 componentDidUpdate
하면 무한 루프 가 생성됩니다 (명백합니다)
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>
);
}
});
답변
setState
내부에서 사용할 수 있습니다 componentDidUpdate
. 문제는 중단 조건이 없기 때문에 어떻게 든 무한 루프를 만들고 있다는 것입니다.
구성 요소가 렌더링되면 브라우저에서 제공하는 값이 필요하다는 사실을 기반으로 사용 componentDidUpdate
에 대한 접근 방식 이 올바르다 고 생각합니다 setState
.
답변
componentDidUpdate
서명입니다 void::componentDidUpdate(previousProps, previousState)
. 이것으로 어떤 소품 / 상태가 더러워 졌는지 테스트하고 setState
그에 따라 호출 할 수 있습니다 .
예:
componentDidUpdate(previousProps, previousState) {
if (previousProps.data !== this.props.data) {
this.setState({/*....*/})
}
}
답변
setState
내부에서 사용 componentDidUpdate
하면 구성 요소가 업데이트 componentDidUpdate
되어 이후에 setState
다시 호출 되어 무한 루프가 발생합니다. 조건부로 호출 setState
하고 호출을 위반하는 조건이 결국 발생하는지 확인해야합니다. 예 :
componentDidUpdate: function() {
if (condition) {
this.setState({..})
} else {
//do something else
}
}
경우에만 (이 componentDidUpdate 안에있는 경우를 제외하고, setState를 업데이트되지 않는) 여기에 소품을 전송하여 구성 요소를 업데이트하는, 당신이 호출 할 수있는 setState
내부 componentWillReceiveProps
대신 componentDidUpdate
.
답변
이 예제는 React Life Cycle Hooks 를 이해하는 데 도움이됩니다 .
당신은 할 수 setState
에서와 getDerivedStateFromProps
즉, 방법 static
및 소품으로 변경 한 후 방법을 트리거 componentDidUpdate
.
에서 componentDidUpdate
당신은 얻을 것이다 3 에서 반환 PARAM을 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 React가 무한 루프에 빠지게 만들었습니다. 작동 상태를 시도했습니다. 그러나 ref 콜백에서 사용하면 더 간단하고 깨끗한 솔루션을 얻을 수 있다는 것을 알았습니다 .ref 콜백에 인라인 함수를 사용하면 모든 구성 요소 업데이트에 대해 null 문제가 발생합니다. 따라서 ref 콜백에서 함수 참조를 사용하고 상태를 설정하면 다시 렌더링이 시작됩니다.