[javascript] setState() inside of componentDidUpdate()

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 dropdown according to its direction. But using setState inside of the componentDidUpdate creates an infinite loop(which is obvious)

I've found a solution in using getDOMNode and setting classname to dropdown directly, but i feel that there should be a better solution using React tools. Can anybody help me?

Here is a part of working code with getDOMNode (i a little bit neglected positioning logic to simplify code)

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>
        );
    }
});

and here is code with setstate (which creates an infinite loop)

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>
        );
    }
});

This question is related to javascript reactjs ecmascript-6

The answer is


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


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});
}

this.setState creates an infinite loop when used in ComponentDidUpdate when there is no break condition in the loop. You can use redux to set a variable true in the if statement and then in the condition set the variable false then it will work.

Something like this.

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
    }

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

_x000D_
_x000D_
// Child component_x000D_
class Child extends React.Component {_x000D_
  // First thing called when component loaded_x000D_
  constructor(props) {_x000D_
    console.log("constructor");_x000D_
    super(props);_x000D_
    this.state = {_x000D_
      value: this.props.value,_x000D_
      color: "green"_x000D_
    };_x000D_
  }_x000D_
_x000D_
  // static method_x000D_
  // dont have access of 'this'_x000D_
  // return object will update the state_x000D_
  static getDerivedStateFromProps(props, state) {_x000D_
    console.log("getDerivedStateFromProps");_x000D_
    return {_x000D_
      value: props.value,_x000D_
      color: props.value % 2 === 0 ? "green" : "red"_x000D_
    };_x000D_
  }_x000D_
_x000D_
  // skip render if return false_x000D_
  shouldComponentUpdate(nextProps, nextState) {_x000D_
    console.log("shouldComponentUpdate");_x000D_
    // return nextState.color !== this.state.color;_x000D_
    return true;_x000D_
  }_x000D_
_x000D_
  // In between before real DOM updates (pre-commit)_x000D_
  // has access of 'this'_x000D_
  // return object will be captured in componentDidUpdate_x000D_
  getSnapshotBeforeUpdate(prevProps, prevState) {_x000D_
    console.log("getSnapshotBeforeUpdate");_x000D_
    return { oldValue: prevState.value };_x000D_
  }_x000D_
_x000D_
  // Calls after component updated_x000D_
  // has access of previous state and props with snapshot_x000D_
  // Can call methods here_x000D_
  // setState inside this will cause infinite loop_x000D_
  componentDidUpdate(prevProps, prevState, snapshot) {_x000D_
    console.log("componentDidUpdate: ", prevProps, prevState, snapshot);_x000D_
  }_x000D_
_x000D_
  static getDerivedStateFromError(error) {_x000D_
    console.log("getDerivedStateFromError");_x000D_
    return { hasError: true };_x000D_
  }_x000D_
_x000D_
  componentDidCatch(error, info) {_x000D_
    console.log("componentDidCatch: ", error, info);_x000D_
  }_x000D_
_x000D_
  // After component mount_x000D_
  // Good place to start AJAX call and initial state_x000D_
  componentDidMount() {_x000D_
    console.log("componentDidMount");_x000D_
    this.makeAjaxCall();_x000D_
  }_x000D_
_x000D_
  makeAjaxCall() {_x000D_
    console.log("makeAjaxCall");_x000D_
  }_x000D_
_x000D_
  onClick() {_x000D_
    console.log("state: ", this.state);_x000D_
  }_x000D_
_x000D_
  render() {_x000D_
    return (_x000D_
      <div style={{ border: "1px solid red", padding: "0px 10px 10px 10px" }}>_x000D_
        <p style={{ color: this.state.color }}>Color: {this.state.color}</p>_x000D_
        <button onClick={() => this.onClick()}>{this.props.value}</button>_x000D_
      </div>_x000D_
    );_x000D_
  }_x000D_
}_x000D_
_x000D_
// Parent component_x000D_
class Parent extends React.Component {_x000D_
  constructor(props) {_x000D_
    super(props);_x000D_
    this.state = { value: 1 };_x000D_
_x000D_
    this.tick = () => {_x000D_
      this.setState({_x000D_
        date: new Date(),_x000D_
        value: this.state.value + 1_x000D_
      });_x000D_
    };_x000D_
  }_x000D_
_x000D_
  componentDidMount() {_x000D_
    setTimeout(this.tick, 2000);_x000D_
  }_x000D_
_x000D_
  render() {_x000D_
    return (_x000D_
      <div style={{ border: "1px solid blue", padding: "0px 10px 10px 10px" }}>_x000D_
        <p>Parent</p>_x000D_
        <Child value={this.state.value} />_x000D_
      </div>_x000D_
    );_x000D_
  }_x000D_
}_x000D_
_x000D_
function App() {_x000D_
  return (_x000D_
    <React.Fragment>_x000D_
      <Parent />_x000D_
    </React.Fragment>_x000D_
  );_x000D_
}_x000D_
_x000D_
const rootElement = document.getElementById("root");_x000D_
ReactDOM.render(<App />, rootElement);
_x000D_
<div id="root"></div>_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
_x000D_
_x000D_
_x000D_


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({/*....*/})
    }
}

You can use setState inside componentDidUpdate


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.


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to reactjs

Error: Node Sass version 5.0.0 is incompatible with ^4.0.0 TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined raised when starting react app Template not provided using create-react-app How to resolve the error on 'react-native start' Element implicitly has an 'any' type because expression of type 'string' can't be used to index Invalid hook call. Hooks can only be called inside of the body of a function component How to style components using makeStyles and still have lifecycle methods in Material UI? React Hook "useState" is called in function "app" which is neither a React function component or a custom React Hook function How to fix missing dependency warning when using useEffect React Hook? Unable to load script.Make sure you are either running a Metro server or that your bundle 'index.android.bundle' is packaged correctly for release

Examples related to ecmascript-6

"Uncaught SyntaxError: Cannot use import statement outside a module" when importing ECMAScript 6 where is create-react-app webpack config and files? Can (a== 1 && a ==2 && a==3) ever evaluate to true? How do I fix "Expected to return a value at the end of arrow function" warning? Enums in Javascript with ES6 Home does not contain an export named Home How to scroll to an element? How to update nested state properties in React eslint: error Parsing error: The keyword 'const' is reserved Node.js ES6 classes with require