setState() inside of componentDidUpdate()

后端 未结 6 2030
鱼传尺愫
鱼传尺愫 2020-11-28 03:23

I\'m writing a script which moves dropdown below or above input depending on height of dropdown and position of the input on the screen. Also I want to set modifier to dropd

相关标签:
6条回答
  • 2020-11-28 03:54

    The componentDidUpdate signature is void::componentDidUpdate(previousProps, previousState). With this you will be able to test which props/state are dirty and call setState accordingly.

    Example:

    componentDidUpdate(previousProps, previousState) {
        if (previousProps.data !== this.props.data) {
            this.setState({/*....*/})
        }
    }
    
    0 讨论(0)
  • 2020-11-28 03:57

    I would say that you need to check if the state already has the same value you are trying to set. If it's the same, there is no point to set state again for the same value.

    Make sure to set your state like this:

    let top = newValue /*true or false*/
    if(top !== this.state.top){
        this.setState({top});
    }
    
    0 讨论(0)
  • 2020-11-28 04:03

    This example will help you to understand the React Life Cycle Hooks.

    You can setState in getDerivedStateFromProps method i.e. static and trigger the method after props change in componentDidUpdate.

    In componentDidUpdate you will get 3rd param which returns from getSnapshotBeforeUpdate.

    You can check this codesandbox link

    // 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>

    0 讨论(0)
  • 2020-11-28 04:04

    If you use setState inside componentDidUpdate it updates the component, resulting in a call to componentDidUpdate which subsequently calls setState again resulting in the infinite loop. You should conditionally call setState and ensure that the condition violating the call occurs eventually e.g:

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

    In case you are only updating the component by sending props to it(it is not being updated by setState, except for the case inside componentDidUpdate), you can call setState inside componentWillReceiveProps instead of componentDidUpdate.

    0 讨论(0)
  • 2020-11-28 04:09

    You can use setStateinside componentDidUpdate. The problem is that somehow you are creating an infinite loop because there's no break condition.

    Based on the fact that you need values that are provided by the browser once the component is rendered, I think your approach about using componentDidUpdate is correct, it just needs better handling of the condition that triggers the setState.

    0 讨论(0)
  • 2020-11-28 04:19

    I had a similar problem where i have to center the toolTip. React setState in componentDidUpdate did put me in infinite loop, i tried condition it worked. But i found using in ref callback gave me simpler and clean solution, if you use inline function for ref callback you will face the null problem for every component update. So use function reference in ref callback and set the state there, which will initiate the re-render

    0 讨论(0)
提交回复
热议问题