298

I am trying to toggle the state of a component in ReactJS but I get an error stating:

Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

I don't see the infinite loop in my code, can anyone help?

ReactJS component code:

import React, { Component } from 'react';
import styled from 'styled-components';

class Item extends React.Component {
    constructor(props) {
        super(props);     
        this.toggle= this.toggle.bind(this);
        this.state = {
            details: false
        } 
    }  
    toggle(){
        const currentState = this.state.details;
        this.setState({ details: !currentState }); 
    }

    render() {
        return (
            <tr className="Item"> 
                <td>{this.props.config.server}</td>      
                <td>{this.props.config.verbose}</td> 
                <td>{this.props.config.type}</td>
                <td className={this.state.details ? "visible" : "hidden"}>PLACEHOLDER MORE INFO</td>
                {<td><span onClick={this.toggle()}>Details</span></td>}
            </tr>
    )}
}

export default Item;
4
  • 69
    Change this.toggle() to this.toggle or {()=> this.toggle()}
    – learner
    Commented Jan 29, 2018 at 8:53
  • 14
    Another improvement, though unrelated to your issue: Turn toggle(){...} into toggle = () => {...} so you don't need to bind it!
    – Berry M.
    Commented Jan 29, 2018 at 9:16
  • 1
    Thanks @learner. You helped me also. Would you kindly explain the reason behind your solution. What is the difference between those two ?
    – Shamim
    Commented Apr 15, 2020 at 10:27
  • 4
    @Shamim It's the difference between calling an existing function, and passing the reference to a function. It's helpful to understand we are writing code to be displayed and triggered when the user does something, not code to be triggered as soon as the user loads the page. reactjs.org/docs/faq-functions.html Commented May 7, 2020 at 8:42

14 Answers 14

408

That is because you are calling toggle inside the render method which will cause to re-render and toggle will call again and re-rendering again and so on.

This line in your code:

{<td><span onClick={this.toggle()}>Details</span></td>}

You need to make onClick refer to this.toggle instead of calling it.

To fix the issue do this:

{<td><span onClick={this.toggle}>Details</span></td>}
7
  • 16
    I am facing a similar situation, but I need to pass a parameter to toggle, how can this be accomplished? Commented Feb 23, 2018 at 5:02
  • 74
    @NivedithaKarmegam Do onClick={(param) => this.toggle(param)}. This will not fire immediatly (and rerender). This is a callback definition (arrow function).
    – fabpico
    Commented Feb 23, 2018 at 9:28
  • 24
    @FabianPicone tried your method but when i console.log it shows that "param" was passed as the event, should actually do onClick={() => this.toggle(param)} Commented Jul 31, 2018 at 2:44
  • 7
    @iWillGetBetter Yes the first param in onClick is the click event. If you need an additional param you can pass it also onClick={(event) => this.toggle(event, myParam)}.
    – fabpico
    Commented Jul 31, 2018 at 9:26
  • 1
    I have this function closeEditModal = () => this.setState({openEditModal: false}); How to call it in render?
    – Nux
    Commented May 23, 2019 at 11:31
51

Forget about the react first:
This is not related to react and let us understand the basic concepts of Java Script. For Example you have written following function in java script (name is A).

function a() {

};

Q.1) How to call the function that we have defined?
Ans: a();

Q.2) How to pass reference of function so that we can call it latter?
Ans: let fun = a;

Now coming to your question, you have used paranthesis with function name, mean that function will be called when following statement will be render.

<td><span onClick={this.toggle()}>Details</span></td>

Then How to correct it?
Simple!! Just remove parenthesis. By this way you have given the reference of that function to onClick event. It will call back your function only when your component is clicked.

 <td><span onClick={this.toggle}>Details</span></td>

One suggestion releated to react:
Avoid using inline function as suggested by someone in answers, it may cause performance issue. Avoid following code, It will create instance of same function again and again whenever function will be called (lamda statement creates new instance every time).
Note: and no need to pass event (e) explicitly to the function. you can access it with in the function without passing it.

{<td><span onClick={(e) => this.toggle(e)}>Details</span></td>}

https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578

0
43

You should pass the event object when calling the function :

{<td><span onClick={(e) => this.toggle(e)}>Details</span></td>}

If you don't need to handle onClick event you can also type :

{<td><span onClick={(e) => this.toggle()}>Details</span></td>}

Now you can also add your parameters within the function.

3
  • 4
    The event object is automatically sent if nothing is specified. Just include an input parameter in the function that is called. Commented May 24, 2018 at 20:31
  • 3
    {<td><span onClick={() => this.toggle(whateverParameter)}>Details</span></td>} does the trick for me Commented Jul 31, 2018 at 2:42
  • 1
    Downvoted because of the noise about the event object, which has nothing to do with it. Commented Aug 7, 2020 at 13:26
14

I know this has plenty of answers but since most of them are old (well, older), none is mentioning approach I grow very fond of really quick. In short:

Use functional components and hooks.

In longer:

Try to use as much functional components instead class ones especially for rendering, AND try to keep them as pure as possible (yes, data is dirty by default I know).

Two bluntly obvious benefits of functional components (there are more):

  • Pureness or near pureness makes debugging so much easier
  • Functional components remove the need for constructor boiler code

Quick proof for 2nd point - Isn't this absolutely disgusting?

constructor(props) {
        super(props);     
        this.toggle= this.toggle.bind(this);
        this.state = {
            details: false
        } 
    }  

If you are using functional components for more then rendering you are gonna need the second part of great duo - hooks. Why are they better then lifecycle methods, what else can they do and much more would take me a lot of space to cover so I recommend you to listen to the man himself: Dan preaching the hooks

In this case you need only two hooks:

A callback hook conveniently named useCallback. This way you are preventing the binding the function over and over when you re-render.

A state hook, called useState, for keeping the state despite entire component being function and executing in its entirety (yes, this is possible due to magic of hooks). Within that hook you will store the value of toggle.

If you read to this part you probably wanna see all I have talked about in action and applied to original problem. Here you go: Demo

For those of you that want only to glance the component and WTF is this about, here you are:

const Item = () => {

    // HOOKZ
  const [isVisible, setIsVisible] = React.useState('hidden');

  const toggle = React.useCallback(() => {
    setIsVisible(isVisible === 'visible' ? 'hidden': 'visible');
  }, [isVisible, setIsVisible]);

    // RENDER
  return (
  <React.Fragment>
    <div style={{visibility: isVisible}}>
        PLACEHOLDER MORE INFO
    </div>
    <button onClick={toggle}>Details</button>
  </React.Fragment>
  )
};

PS: I wrote this in case many people land here with similar problem. Hopefully, they will like what I have shown here, at least well enough to google it a bit more. This is NOT me saying other answers are wrong, this is me saying that since the time they have been written, there is another way (IMHO, a better one) of dealing with this.

9

if you don't need to pass arguments to function, just remove () from function like below:

<td><span onClick={this.toggle}>Details</span></td>

but if you want to pass arguments, you should do like below:

<td><span onClick={(e) => this.toggle(e,arg1,arg2)}>Details</span></td>
8

1.If we want to pass argument in the call then we need to call the method like below As we are using arrow functions no need to bind the method in constructor.

onClick={() => this.save(id)} 

when we bind the method in constructor like this

this.save= this.save.bind(this);

then we need to call the method without passing any argument like below

onClick={this.save}

and we try to pass argument while calling the function as shown below then error comes like maximum depth exceeded.

 onClick={this.save(id)}
1
  • How do we pass parameters to the function when using "bind"?
    – Sapthaka
    Commented Nov 24, 2021 at 10:06
8

In this case, the following code:

{<td><span onClick={this.toggle()}>Details</span></td>}

causes toggle function to call immediately and re-renders it again and again, thus making infinite calls.

So, passing only the reference to that toggle method will solve the problem.

i.e.

{<td><span onClick={this.toggle}>Details</span></td>}

will be the solution code.

If you want to use the (), you should use an arrow function like this

{<td><span onClick={()=> this.toggle()}>Details</span></td>}

In case you want to pass parameters, you should choose the last option and you can pass parameters as follows:

{<td><span onClick={(arg)=>this.toggle(arg)}>Details</span></td>}

In the last case, it doesn't call the function immediately, so it won't re-render the function, hence avoiding infinite calls.

2
  • Really nice explanation.Thanks a lot Commented Sep 29, 2020 at 5:49
  • This is so crystal clear explanation :) Commented May 23, 2022 at 8:07
4

ReactJS: Maximum update depth exceeded error

inputDigit(digit){
  this.setState({
    displayValue: String(digit)
  })

<button type="button"onClick={this.inputDigit(0)}>

why that?

<button type="button"onClick={() => this.inputDigit(1)}>1</button>

The function onDigit sets the state, which causes a rerender, which causes onDigit to fire because that’s the value you’re setting as onClick which causes the state to be set which causes a rerender, which causes onDigit to fire because that’s the value you’re… Etc

2

onClick you should call function, thats called your function toggle.

onClick={() => this.toggle()}

2

Recently I got this error:

Error: Minified React error #185; visit https://reactjs.org/docs/error-decoder.html?invariant=185 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

The full text of the error you just encountered is:

Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

Ok. Here is my case, I use react function component + react hooks. Let's see the incorrect sample code first:

import { useEffect, useState } from "react";
const service = {
  makeInfo(goods) {
    if (!goods) return { channel: "" };
    return { channel: goods.channel };
  },
  getGoods() {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve({
          channel: "so",
          id: 1,
          banners: [{ payway: "visa" }, { payway: "applepay" }]
        });
      }, 1000);
    });
  },
  makeBanners(info, goods) {
    if (!goods) return [];
    return goods.banners.map((v) => {
      return { ...v, payway: v.payway.toUpperCase() };
    });
  }
};
export default function App() {
  const [goods, setGoods] = useState();
  const [banners, setBanners] = useState([]);

  useEffect(() => {
    service.getGoods().then((res) => {
      setGoods(res);
    });
  }, []);

  const info = service.makeInfo(goods);

  useEffect(() => {
    console.log("[useEffect] goods: ", goods);
    if (!goods) return;
    setBanners(service.makeBanners({}, goods));
  }, [info, goods]);

  return <div>banner count: {banners.length}</div>;
}

service - process API call, and has some methods for converting DTO data view model. It has nothing to do with React. Maybe you have a service like this in your project.

My logic is that the banners view model constructs from the goods data returned from the API.

useEffect({...}, [info, goods]) has two dependencies: info and goods.

When info and goods change, useEffect hook will re-execute, set banners view model, it looks good, right?

No! It will cause a memory leak. The useEffect hook will execute infinitely. Why?

Because when setBanner() executed, the component will re-render, the const info = service.makeInfo(goods); statement will execute again, returns a new info object, this will lead to the change of the useEffect's deps, causing useEffect to execute again, forming a dead cycle.

Solutions: use useMemo returns a memorized value. Use this memorized value as the dependency of the useEffect hook.

// ...
 const info = useMemo(() => {
    return service.makeInfo(goods);
  }, [goods]);

  useEffect(() => {
    console.log("[useEffect] goods: ", goods);
    if (!goods) return;
    setBanners(service.makeBanners({}, goods));
  }, [info, goods]);

//... 

Codesandbox

0

Many good answer but all missing some example considering hooks in react/ React native

As wrote in an answer above is that the callback should not be "called" inside the child Component but only referenced.

What this means? Let's consider a Parent Component that take 2 Children Components that change rgb color for change color on press:

import React, { useState } from "react"
import {View, Text, StyleSheet } from "react-native"
import ColorCounter from "../components/ColorCounter"

const SquareScreen = () =>{
  const [red, setRed] = useState(0)
  const [blue, setBlue] = useState(0)
  const [green, setGreen] = useState(0)

 return (
   <View>
     <ColorCounter 
       onIncrease={() => setRed(red + 15)}
       onDecrease={() => setRed(red - 15)}
       color="Red"
     />
     <ColorCounter 
       onIncrease={() => setBlue(blue + 15)}
       onDecrease={() => setBlue(blue - 15)} 
       color="Blue" 
     />
     <ColorCounter 
       onIncrease={() => setGreen(green + 15)}
       onDecrease={() => setGreen(green - 15)}
       color="Green" 
     />
    <View 
      style={{ 
        height:150,
        width:150, 
        backgroundColor:`rgb(${red},${blue},${green})`
      }}
    />
    </View>
 )   
}

const styles = StyleSheet.create({})

export default SquareScreen

This is the Child Button Component:

import React, { useState } from "react"
import {View, Text, StyleSheet, Button } from "react-native"

const ColorCounter = ({color, onIncrease, onDecrease}) =>{
  return (
    <View>
      <Text>{color}</Text>
      <Button onPress={onIncrease}  title={`Increase ${color}`} /> --> here if you use onPress={onIncrease()} this would cause a call of setColor(either setRed,SetBlue or setGreen) that call again onIncrease and so on in a loop)
      <Button onPress={onDecrease}  title={`Decrease ${color}`} />
    </View>  
  )  
}

export default ColorCounter
0

when i use useEffect like this useEffect(() =>{}), add [] to it, like this useEffect(() => {},[]).

0

in my case it was happening due to infinite loop.

my one function was setting state and the component was getting reload and that component in turn was causing function to re run and then function was again setting the state.. and the loop was going on to infinity..

to avoid this situation i wrapped my function in useEffect hook and gave it some dependency so that this function run only if the dependency gets changed.

0

Just remove the () but if it the case of useEffect then

const [isInitialRender, setIsInitialRender] = useState(true);

useEffect(() => {
    const data = localStorage.getItem("auth");
      if(isInitialRender){
        setIsInitialRender(false);
        if (data) {
          const parsed = JSON.parse(data);
          setAuth({...auth, user: parsed.user, token: parsed.token });
        }
      }
  }, [auth, isInitialRender]);

isInitialRender true and false will avoid you to fall into loop

Not the answer you're looking for? Browse other questions tagged or ask your own question.