componentDidUpdate () 내부의 setState ()


131

드롭 다운의 높이와 화면의 입력 위치에 따라 드롭 다운을 입력의 위 또는 아래로 이동시키는 스크립트를 작성 중입니다. 또한 방향에 따라 수정자를 드롭 다운하도록 설정하고 싶습니다. 그러나 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>
        );
    }
});

9
여기서 트릭 setState항상 다시 렌더링을 트리거하는 것 입니다. 여러 번 확인 state.top하고 호출 setState하는 대신 state.top로컬 변수에 포함 하려는 항목 을 추적 한 다음 로컬 변수가 일치하지 않는 경우에만 componentDidUpdate호출 이 끝날 때 한 번만 추적 setState하십시오 state.top. 지금 당장, state.top첫 번째 다시 렌더링 후 즉시 재설정 하면 무한 루프가 발생합니다.
랜디 모리스

2
이 바이올린componentDidUpdate 에서 두 가지 다른 구현을 참조하십시오 .
랜디 모리스

젠장! 지역 변수는 전체 문제를 해결합니다. 어떻게 mysef가 알아 내지 못했습니까! 감사합니다!
Katerina Pavlenko

1
아래 답변을 수락해야한다고 생각합니다. 다시 읽으면 초기 질문에 충분히 답할 수 있다고 생각합니다.
랜디 모리스

왜 아무도 조건을 바꾸라고 제안하지 않았 componentShouldUpdate습니까?
패트릭 로버츠

답변:


116

setState내부에서 사용할 수 있습니다 componentDidUpdate. 문제는 중단 조건이 없기 때문에 어떻게 든 무한 루프를 만들고 있다는 것입니다.

구성 요소가 렌더링되면 브라우저에서 제공하는 값이 필요하다는 사실을 기반으로 사용 componentDidUpdate에 대한 접근 방식 이 올바르다 고 생각합니다 setState.


4
'파단 조건'이란 무엇입니까? 상태가 이미 설정되어 있는지 확인하고 재설정하지 않습니까?
Katerina Pavlenko

나는 이것에 동의한다. 내 유일한 추가 의견은 클래스 추가 / 제거가 불필요 componentDidUpdate하고 render대신 필요에 따라 추가 될 수 있다는 것입니다.
랜디 모리스

그러나 클래스 추가 / 제거는 componentDidUpdate에서 체크인 된 드롭 다운 위치에 따라 다릅니다. 두 번 확인 하시겠습니까? 그리고 내가 이해하는 것처럼 componentDidUpdate는 AFTER render ()라고 불리우므로 render ()에서 클래스를 추가 / 제거하는 것은 쓸모가 없습니다.
Katerina Pavlenko

setstate로 코드를 추가했습니다. 확인하고 실수 할 수 있습니까? 루프를 일으키지 않는 예를 보여주세요
Katerina Pavlenko

2
componentDidUpdate (prevProps, prevState) {if (prevState.x! == this.state.x) {// 무언가를해라}}
Ashok R

68

componentDidUpdate서명입니다 void::componentDidUpdate(previousProps, previousState). 이것으로 어떤 소품 / 상태가 더러워 졌는지 테스트하고 setState그에 따라 호출 할 수 있습니다 .

예:

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

componentDidMount인수가 없으며 구성 요소가 작성 될 때만 호출되므로 설명 된 목적으로 사용할 수 없습니다.
Jules

@ 줄스 감사합니다! 나는 componentDidMount썼다. 그래서 나는 대답을 썼을 때 유명한 이름이 계단식으로 😮 다.
Abdennour TOUMI

componentDidUpdate(prevProps, prevState) { if ( prevState.x!== this.state.x) { //Do Something } }
Ashok R

나는 당신의 관심사 @AshokR을 알고 있습니다. 인수 이름을 줄입니다. 그러나 "이전"은 이전이 아닌 것을 방지 할 수 있습니다.. .kidding :)
Abdennour TOUMI

58

setState내부에서 사용 componentDidUpdate하면 구성 요소가 업데이트 componentDidUpdate되어 이후에 setState다시 호출 되어 무한 루프가 발생합니다. 조건부로 호출 setState하고 호출을 위반하는 조건이 결국 발생하는지 확인해야합니다. 예 :

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

경우에만 (이 componentDidUpdate 안에있는 경우를 제외하고, setState를 업데이트되지 않는) 여기에 소품을 전송하여 구성 요소를 업데이트하는, 당신이 호출 할 수있는 setState내부 componentWillReceiveProps대신 componentDidUpdate.


2
이전 질문이지만 componentWillReceiveProps는 더 이상 사용되지 않으며 componentWillRecieveProps를 사용해야합니다. 이 메소드 안에서는 state를 설정할 수 없습니다.
Brooks DuBois

당신은 의미 getDerivedStateFromProps합니다.
adi518

5

이 예제는 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>


2

상태가 이미 설정하려는 값과 동일한 지 확인해야한다고 말하고 싶습니다. 동일하면 동일한 값으로 상태를 다시 설정할 지점이 없습니다.

다음과 같이 상태를 설정하십시오.

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

-1

툴팁을 가운데에 배치 해야하는 비슷한 문제가있었습니다. componentDidUpdate의 setState React가 무한 루프에 빠지게 만들었습니다. 작동 상태를 시도했습니다. 그러나 ref 콜백에서 사용하면 더 간단하고 깨끗한 솔루션을 얻을 수 있다는 것을 알았습니다 .ref 콜백에 인라인 함수를 사용하면 모든 구성 요소 업데이트에 대해 null 문제가 발생합니다. 따라서 ref 콜백에서 함수 참조를 사용하고 상태를 설정하면 다시 렌더링이 시작됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.