DEV Community

Brian Neville-O'Neill
Brian Neville-O'Neill

Posted on • Originally published at blog.logrocket.com on

An imperative guide to setState in React

States in React, like props, are objects that are used to store data and affect how the component renders or behaves. Unlike props, states are managed completely within the component and can be changed over time.

The change in state for React components can be a result of triggers from user actions, network activity, API requests or specific application behavior.

Components that have a state are referred to as stateful components while those that do not have states are referred to as stateless components.

A component can have an initial state set, access it and also update it:

import React, { Component } from 'react';

class Food extends Component {
  constructor(props) {
    super(props)

    this.state = {
      fruits: ['apple', 'orange'],
      count: 0
    }
  }
}

In the above code block, we are setting the component’s initial state. This is done through the constructor method.

As mentioned previously, states are plain Javascript objects which is why this.state should be equal to an object:

this.state = {
  fruits: ['apple', 'orange'],
  count: 0
}

Accessing component states

Component states can be accessed like other objects using this.state.property_name.

To access the count in the above example, we can do this by this.state.count :

render() {
  return (
    <div className = "container">
      <h2> Hello!!!</h2>
      <p> I have {this.state.count} fruit(s)</p>
    </div>
  );
}

Updating component states

Although it is technically possible to write to this.state from anywhere in your code, it’ll not prompt a re-render, and this would lead to unstable and incorrect state values when you try to access the values through this.state.

The only place you should directly write to this.state is the component’s constructor method.

Use thesetState() method everywhere else, doing so will accept an object that will eventually be merged into the component’s existing state.

For example, this will not re-render a component:

// Wrong
this.state.name = 'Obaseki Nosa';

Instead, use setState().

Introducing setState()

setState() schedule changes to the component’s state object and tells React that this component and its children need to be re-rendered with the update state:

// Correct
this.setState({name: 'Obaseki Nosa'});

React intentionally “waits” until all components call setState() in their event handlers before starting to re-render. This boosts performance by avoiding unnecessary re-renders.

Know that setState() can be considered as a request instead of an immediate command to update the component.

This is why trying to use this.state immediately after a setState() would lead to incorrect behaviors:

// Trying to change the value of this.state.count from previous example
this.setState({
  count: 4
});

console.log(this.state.count); // 0

this.state.count returns 0 because even though the value has been set with setState(), it was only scheduled and yet to be re-rendered before attempting to use the value with this.state.

setState() will always lead to a re-render unless shouldComponentUpdate() returns false.

Using setState() in React lifecycle methods

Calling setState() in React’s lifecycle methods requires a certain level of caution. There are a few methods where calling setState() would lead to undesirable results and others where it should be avoided completely.

render()

Calling setState() here makes your component a contender for producing infinite loops.

The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it’s invoked, and it does not directly interact with the browser.

In which case, avoid using setState() here.

constructor()

You should not call setState() in the constructor(). Instead, if your component needs to use local state, assign the initial state to this.state directly in the constructor.

componentDidMount()

componentDidMount() is invoked immediately after a component is mounted. You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen thus render() will be called twice.

componentDidUpdate()

componentDidUpdate() is invoked immediately after updating occurs. You may call setState() immediately here but know that it must be wrapped in a condition like in the example below, or you’ll cause an infinite loop:

componentDidUpdate(prevProps, prevState) {
  let newName = 'Obaseki Nosa'
  // Don't forget to compare states
  if (prevState && prevState.name !== newName) {
    this.setState({name: newName});
  }
}

componentWillUnmount()

You should not call setState() here because the component will never be re-rendered. Once a component instance is unmounted, it will never be mounted again.

Conclusion

  1. setState() is async, meaning there is no guarantee that the state has been updated if we try to access the value immediately
  2. You can only change state with setState and React will react to the change 😉
  3. Avoid confusing the state object with other instance properties. It’s easy to assume you can define another object in the constructor and try to use it like state but the state instance is a special one because React will manage it:
...
//constructor function above

this.state = {
  fruits: ['apple', 'orange'],
  count: 0
}

this.user = {
  name: 'Obaseki Nosa'
}

...

Although both this.state and this.user are objects initialized in the constructor, only this.state reacts with setState() and is managed by React.

Cheers!!!


Plug: LogRocket, a DVR for web apps

https://logrocket.com/signup/

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single page apps.

Try it for free.


The post An imperative guide to setState in React appeared first on LogRocket Blog.

Top comments (0)