Professional Development

Updating State in React

React is a beast like no other. While I found learning node.js to be pretty straight-forward with some JavaScript experience, React has a lot of functionality obscured, making it harder to learn and understand right out of the gate. Further complicating things is that React is relatively young (initial release May 2013), and has changed significantly over the years. This means best practices from only a few years ago may be already outdated or even deprecated.

Today I want to go over one basic function: updating state. State is a critical concept in React, and updating state is one of the basic functions you will perform regularly in any React application. But what is the best way to do it?

For example, take the given App.js file:

import React from 'react';
import Header from './Header';
import Navigation from './navigation/Navigation';
import Grid from './Grid';
import Cart from './Cart';
import Footer from './Footer';

class App extends React.Component {
  state = {
    inventory: {},
    cart: {},
  };

  render() {
    return (
      <main>
        <Header />
        <Navigation />
        <Grid inventory={this.state.inventory} addToCart={this.addToCart} />
        <Footer />
      </main>
    )
  }
}

export default App;

I want to create the addToCart function, which will update the state cart property by setting the key to 1 if it’s not in the cart yet, or adding 1 to the current value if it is in the cart. The key corresponds to the id of the inventory item added to the cart. The way I first learned this is:

addToCart = (key) => {
  const cart = { ...this.state.cart };
  cart[key] = cart[key] + 1 || 1;
  this.setState({ cart });
};

This seems all well and good but then I heard you should never use this.state in an update function because setState is asynchronous, and you could have some issues with this.state not actually referring the current state. Recommended is using prevState in a callback function, as so:

addToCart = (key) => {
  this.setState(prevState => {
    let cart = { ...prevState.cart }; 
    cart[key] = cart[key] + 1 || 1;                                  
    return { cart };
  });
};

Now this looks pretty nifty, and resolves any issues with asynchronous updates, but it can actually be refactored as so:

addToCart = (key) => {
  this.setState(prevState => ({
    cart: { ...prevState.cart, [key]: prevState.cart[key] + 1 || 1 }
  }))
};

Many ways to skin the cat, and not easy to find the best way.

Leave a comment