setState() Gate

Navigating React setState() Behavior Confusion

Eric Elliott
Mar 19, 2017 · 12 min read
Image for post
Image for post
“React has a setState() problem: Asking newbies to use setState() is a recipe for headaches. Advanced users have learned to avoid it. ;)
“React team member checking in. Please learn to use setState before other approaches.”
“Those ‘adanced’ users will get left behind when we turn on async scheduling by default in React 17”
“Fiber has a strategy for pausing, splitting, rebasing, aborting updates that doesn’t work if you deviate from component state”
Image for post
Image for post

“React has a setState() problem: Asking newbies to use setState() is a recipe for headaches. Advanced users have secret cures.”

Of course, Twitter would probably still lose its collective mind. After all, React is perfect, and we must all agree that setState() is beautiful just as it is, or face ridicule and scorn.

What’s Wrong with setState()?

This question has two answers:

  1. Learning curve. Users new to React and setState() frequently encounter obstacles while trying to do things that just work with vanilla JS and direct DOM manipulation.
  • You can’t just set the state to anything at any time, depending on any data source you like.
  • You can’t just observe the rendered DOM or element visibility on screen at any part of the component lifecycle, which limits when and how you can use setState() for render dependent state (the state you’re working on may not have been rendered to the screen, yet).

Dependent State

When we’re updating state, sometimes the value of the update depends on things that React tries to help us with:

  • Previous attempts to update state in the same cycle
  • The rendered DOM (e.g., component coordinates, visibility, calculated CSS values, etc…)
setState(nextState, callback)
// assuming state.count === 0
this.setState({count: state.count + 1});
this.setState({count: state.count + 1});
this.setState({count: state.count + 1});
// state.count === 1, not 3
Object.assign(state,
{count: state.count + 1},
{count: state.count + 1},
{count: state.count + 1}
); // {count: 1}
// Correct
this.setState((prevState, props) => ({
count: prevState.count + props.increment
}));
[
{increment: 1},
{increment: 1},
{increment: 1}
].reduce((prevState, props) => ({
count: prevState.count + props.increment
}), {count: 0}); // {count: 3}
(prevState, props) => ({
count: prevState.count + props.increment
})

Just A Newbie Problem?

I still bump into rough edges now and then when I’m dealing with forms or DOM element coordinates because, when you use setState(), you have to deal with the component lifecycle directly. When you use a container component or store and pass your state through props, React handles the timing issues for you.

  • Employing immutability to eliminate shared mutable state, which allows unrestricted access to state, and new state creation at any point in time. (e.g., Redux)

Decide the State Before You Render

Moving state management to a container component (or Redux) forces you to think differently about your component state by making it clear that before you can render a component, its state must already be decided (because you have to pass it in as props).

Before you render, decide the state!

An obvious corollary is that trying to use setState() inside your render() method is an anti-pattern.

How Can setState() be Fixed?

My preference would be to deprecate the object literal form of setState(). I know it’s (superficially) easier to understand and more convenient, but it’s also how a lot of new users get stuck, and I think it’s self-evident that somebody doing this:

state.count; // 0
this.setState({count: state.count + 1});
this.setState({count: state.count + 1});
this.setState({count: state.count + 1});

Why is setState() so Strict?

The quirky behavior of setState() is not a bug. It’s a feature. In fact, you might say it’s the whole reason that React exists in the first place.

Image for post
Image for post
“You keep using that word. I don’t think it means what you think it means.”

When Should We Use setState()?

I use setState() almost exclusively for self-contained units of functionality that don’t need to persist state. In other words, things like reusable form validation components, custom date or time block selection widgets, data visualization widgets that let you customize their view state, etc…

  • Do you need to persist the state? (Save it to local storage or send it to a server?)
Image for post
Image for post
“Those who are unaware they are walking in darkness will never seek the light”.

Next Steps

Want to learn a whole lot more about building software with React and Redux?

Image for post
Image for post

JavaScript Scene

JavaScript, software leadership, software development, and…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store