[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 콜백에서 함수 참조를 사용하고 상태를 설정하면 다시 렌더링이 시작됩니다.


답변