React: Calling setState in render causes infinite loop

Created on 3 Dec 2015  ·  26Comments  ·  Source: facebook/react

This may seem really silly to do to call setState in render. However it's possible for this to happen if a component has a callback which is being called immediately during render and in your callback handler you call setState.

Most helpful comment

I ran into this by accident once... I had onClick={this.setState({inProgress: true})} instead of onClick={() => this.setState({inProgress: true})}. It would be nice if React detected the problem and threw an error "setState detected in render() of SomeComponent" instead of going into the infinite loop.

All 26 comments

In general, this should never happen; you should not be doing anything remotely related to modifying any component's state within a render function.

I'm curious what your use case is, for having a callback which does a setState; mind sharing?

I ran into this by accident once... I had onClick={this.setState({inProgress: true})} instead of onClick={() => this.setState({inProgress: true})}. It would be nice if React detected the problem and threw an error "setState detected in render() of SomeComponent" instead of going into the infinite loop.

A video player is playing. I have an Component which sets styles on the scrub bar. When the Component that sets styles updates it fires a callback. (there's a good reason for this but it's beyond explaining in a simple issue) I was listening to this callback and calling setState to notify my component changes happened.

In general setState isn't evaluated until before the next "render" but in this case setState calls immediately which is divergent behaviour from the normal usage of setState.

If I'm understanding your flow correctly, I still don't see how/why a setState would be occurring within a component's render() function. Do you just mean within a single reconciliation cycle? Keep in mind that render() is not recursive (that's a common miss-conception; the child's render function is not called during the parent's render function).

@mikkoh wrote:

When the Component that sets styles updates it fires a callback.

I assume you're using componentDidUpdate for this? Can you provide a simple JSFiddle example/testcase that demonstrates the error you're running into?

This is an oversimplification of the code but:

handleSomething(value) {
  this.setState({
    something: value
  });
}

render() {
  return <Foo 
    someProp={this.props.someValue}
    onCallback={this.handleSomething} 
  />;
}

When Foo receives props it calls onCallback which in turn calls handleSomething. Which will cause render to be called immediately. Then once again handleSomething gets called then render and so on.

Again this is an oversimplification and what Foo might be doing is dumb but react still shouldn't call render immediately after setState in the same reconciliation cycle.

Again this is an oversimplification and what Foo might be doing is dumb but react still shouldn't call render immediately after setState in the same reconciliation cycle.

The fact that we support calling setState in componentDidMount or componentDidUpdate is not an accident – doing so in order to trigger an update is sometimes useful.

@mikkoh Did you find a workaround?
just came across this exact issue..

I ended up implementing process.nextTick where the callback gets called. This is a workaround and not a fix.

@spicyj setState was not being called called in componentDidMount or componentDidUpdate but rather in a callback that resulted/was called because a component being rendered.

@mikkoh Can you provide a specific code example here? I'm not sure I understand exactly what your issue was.

@spicyj I posted a snippet earlier. So below when Foo renders it will call this.handleSomething via props.onCallback. Now it's unfortunate cause I can't remember remember at which stage of Foo's render lifecycle props.onCallback gets called (render, componentWillMount, etc) But basically the simple act of Foo being rendered called props.onCallback.

handleSomething(value) {
  this.setState({
    something: value
  });
}

render() {
  return <Foo 
    someProp={this.props.someValue}
    onCallback={this.handleSomething} 
  />;
}

I'm still failing to understand why what you're doing causes an infinite loop but that you say adding a process.nextTick alleviates the problem. In either case, if you call setState repeatedly, the component should continue to rerender.

If you can post a working code example (in jsbin, for example) that shows the problem, I'm happy to take another look – but as is since I can't understand what you're doing this is not very actionable for me. I don't understand what the intention of your code is if you're trying to update the parent's state whenever a child renders. Wouldn't you expect that to cause an infinite loop?

I realize this is an old ticket and there might be more idiomatic ways of doing things in React now - but I just hit an issue which is related, so figured here's as good a place as any to comment ;)

What's the best way to handle situations where you need to measure _after_ the component has been mounted? I'd prefer to avoid ref because there's no need to actually call the component. Also, when dealing with custom renderers I'm not sure how to implement ref and furthermore - CSS tricks like 'visible' may not apply.

Here's an example of what I mean:

class MyComponent extends React.Component {
    private instanceWidth:number;

    constructor(props) {
        super(props)
        this.state = {}
    }

    componentDidMount() {
        this.setState({
            instanceWidth: this.instanceWidth
        })
    }

    render() {
        return <SomeElement 
            onAdded={instance => this.instanceWidth = instance.width}
            x={this.state.instanceWidth === undefined ? 0 : window.innerWidth - this.state.instanceWidth}
            {...props}
        />
    }
}

The issues I have with this approach are:

  1. It's _bordering_ on like setState within render. Not really since it will only happen the first time via cDM, but still... feels icky with that extra private var and... I dunno... just feels odd?

  2. There's going to be a blip where the wrong position is truly rendered. The only way around this I can think of is to manage it via visual tricks like setting opacity.

I'd love to hear thoughts on why this is or isn't the right way to do it. Thanks!

  1. It is absolutely fine to call setState from componentDidMount if you have to read something from the DOM. It's not supposed to be pure. In fact the measurement use case is one of the reasons it exists.

  2. React was designed with this use case in mind. There shouldn't be a blip because React processes setState from componentDidMount synchronously to avoid this problem.

So so late to the party here but @sophiebits's comment about componentDidMount has just brought an end to 3 hours of debugging - thank you!

I've a strange issue.
I have big forms who are rendering with state dropdowns. And i've to check when populating the dropdowns if a value exist. And if she exist, update the child dropdown with the right values.
I store all changements in a variable in componentDidMount state I update each dropdown who are stored in my variable. but it cause an infinite loop...

@spyx08 can we see a code sample?

Here's what will cause an infinite loop, assuming you're not doing any state change checks:

  • setState inside componentWillUpdate
  • setState inside componentDidUpdate
  • setState inside render (this is usually accidental)
  • setState inside getSnapshotBeforeUpdate

Make sure you're not doing any of the above and you shouldn't see an infinite loop.

I am also facing a similar issue as @mikkoh : where I'm passing down a parent function through props, to be called from a button inside a child component; which is going into an infinite loop.

handleSomething(value) {
this.setState({
something: value
});
}

render() {
return someProp={this.props.someValue}
onCallback={this.handleSomething}
/>;
}

where I am only calling this.props.onCallback from a button inside my child.

Help!

@lisadesouza89, all depends on how exactly your are calling the this.prrops.onCallback inside the Button. Could you provide this information?

Parent component:

constructor{...
this.initModal = this.initModal.bind(this);
}
initModal = (localRecord) => {
        console.log("Called method with record:"+JSON.stringify(localRecord));
        this.setState({record:localRecord,modalIsOpen:true});       
    };
return{
<Child1 mRecord={mRecord} init={this.initModal}/>
}

Child1:

render{
            <span>
                    <Child2 mRecord={this.props.mRecord} init={this.props.init}/>
                </span>
            </div>
            );
}

Child2:

render{
<div>
       <button onClick={this.props.init(this.props.mRecord)}/>
</div>
}

You shouldn't call init within onClick declaration. Since you know mRecord within parent you could bind it to initModal inside parent's constructor.

//in parent
this.initModal = this.initModal.bind(this, mRecord);
// in child 2
<button onClick={this.props.init}/>

There are multiple Child1 and Child2 elements with different values, so no, I don't know mRecord in the parent.
But I figured out my issue: I should have
<button onClick={()=>this.props.init(this.props.mRecord)}/>
instead of
<button onClick={this.props.init(this.props.mRecord)}/>

how to use react native flex radio button inside for loop

I have a simple solution, compares the state before you change, it worked for me.
I just "setState" the state if the new state is different than the current.

I have developed a picker for setting day and night. I getting the current time for a picker value from an API and trying to set with setState, but every time it will display 'day' as a value. I want it should display based on the value passed. How can I solve the issue here

 this.state= {
    time: null
}

  timeMode = (Currenttime) => {   //here if the passed value is 'night' then it should set the value to 
night.  Then when clicking on picker it should be both values in dropdown

  this.setState({ time: Currenttime }); // this causes infinite loop...
  return (
    <View style={Styles.paddingStyle}>
        <Picker
            selectedValue={this.state.time}
            mode="dropdown"
            onValueChange={(value) => this.setState({time: value})}   >
            {this.DropdownValues()}
        </Picker>
    </View>
   )
}

DropdownValues = () => {
const time = [
  'day',
  'night'
];
return time.map(data => {
  return <Picker.Item label={data} value={data} />;
});
};

I ran into this by accident once... I had onClick={this.setState({inProgress: true})} instead of onClick={() => this.setState({inProgress: true})}. It would be nice if React detected the problem and threw an error "setState detected in render() of SomeComponent" instead of going into the infinite loop.

https://github.com/facebook/react/issues/18424

I was just passing my lockdown times learning react. I came into a similar situation. So react does go into an infinite loop when I call setState directly inside the render, but react is smart enough to detect it and stops, throwing and error that something dumb has happend. But if I do a dynamic import and call setState inside then(), it keeps on rendering, and it got my CPU fans roaring... any reasons why it doesn't detect "Maximum update depth exceeded" when setState is called in such a way?

Was this page helpful?
0 / 5 - 0 ratings