How to Simplify state management in React apps with batched updates

How to Simplify state management in React apps with batched updates

In this post, we have taken a closer look at the batched updates feature and demonstrated how it simplifies the statement management and rendering process in React applications.

After making an update to your component’s state using either useState or this.setState, parts of the component re-renders depending on the update. More so, if you have various calls to update the state within a React event handler like onClick, React makes the updates in a batch, instead of one at a time, reducing the number of renders the component will make.

However, you might not always be making a call to update the state within an event handler and in these cases (for example within a Promise or a SetTimeout), React makes the updates synchronously instead of in a batch. This means that you’ll get multiple re-renders. 

import React, { Fragment, useState } from "react";
import ReactDOM from "react-dom";
function Component() {
  const [item1, setItem1] = useState("Initial Item 1");
  const [item2, setItem2] = useState("Initial Item 2");
  console.log("render: ", item1, item2);
  function handleClickWithPromise() {
    Promise.resolve().then(() => {
      setItem1("Updated Item 1");
      setItem2("Updated Item 2");
    });
  }
  function handleClickWithoutPromise() {
    setItem1("Updated Item 1");
    setItem2("Updated Item 2");
  }
  return (
    <Fragment>
      <button onClick={handleClickWithPromise}>
        {item1} - {item2} (with promise)
      </button>
      <button onClick={handleClickWithoutPromise}>
        {item1} - {item2} (without promise)
      </button>
    </Fragment>
  );
}
function App() {
  return <Component />;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

In this example, we have two state values item1 and item2, and we update their values when any of the two buttons are clicked. However, in the first button, we make the updates within a Promise.

By default, React batches updates made in a known method like the lifecycle methods or event handlers, but doesn’t do the same when the updates are within callbacks like in SetTimeout or Promises. This means that if you have multiple calls to update the state, React re-renders the component each time the call is made.

When the Component first renders, the console outputs the initial values of item1 and item2.

Then, if you click on the first button the component re-renders twice and you see the initial render and then the two subsequent re-renders:

But if you refresh the page and click on the second button, you see the initial render and only one re-render even though there are still two updates made to the state:

Why was that possible? Well, because React automatically batches updates in known methods, in this case, an event handler. 

When you have multiple state calls wrapped in a Promise like in the example above, you can force React to make batched updates, hence causing only one re-render. This can be done by wrapping the calls to update the state in *ReactDOM.unstable_batchedUpdates()* like this:

function handleClickWithPromise() {
    Promise.resolve().then(() => {
      ReactDOM.unstable_batchedUpdates(() => {
        setItem1("Updated Item 1");
        setItem2("Updated Item 2");
      });
    });
  }

Next, update the handleClickWithPromise() function on the previous demo with the snippet above like we currently have on this Sandbox. Now, if you click on the first button, the updates will be batched, causing only one render. If you look at your console you should see this after you click on the first button:

This is unlike the last time where we had two re-renders on clicking the first button (with promise). As a result of wrapping the calls to update state in *ReactDOM.unstable_batchedUpdates()*, we get the same exact functionality we had with the second button (without promise).

As I mentioned earlier, if you use the *unstable_batchedUpdates()* keep in mind that it’s an implementation detail. Future versions of React will probably make this the default behaviour and you wouldn’t have to use the unstable API.

Should you use it?

The name of the method does make it a bit concerning whether it’s safe to use in production. However, the React team has previously encouraged (and at the time of writing, still do) the use of this API when appropriate. So, it’s safe to say that although “unstable” it is stable enough to be used in production today.

When to use it

If you need to make multiple calls to update the state like in the examples above, you might find that there’s a better way to do it. In my experience, most cases where I’ve seen developers make multiple calls to update the state, those calls could have easily been replaced with a single call. Let’s take a look at some instances where you might make multiple calls to update the state.

this.setState({ ...this.state, foo: 42 });
if (condition) {
    this.setState({ ...this.state, isBar: true });
}

The above code could be refactored to update the state with a single call like so:

let newState = { this.state, foo: 42 };
if (condition) {
    newState = { ...this.state, isBar: true };
}
this.setState(newState);

Of course, you are creating a whole new variable, and that is okay. Usually, as we saw earlier, React would automatically batch the updates made in certain functions and not in others. As a result, you should be deliberate about when you try to reduce the number of calls to setState.

Another instance where you would have multiple calls to update the state is:

// Increment foo
this.setState({ ...this.state, foo: this.state.foo + 1 });
this.setState({ ...this.state, foo: this.state.foo + 1 });

In this case, the subsequent calls make use of the updated values made by previous calls. Again, the above code can be refactored like so:

function incrementFooBy(delta) {
    return (previousState, currentProps) => {
        return { ...previousState, foo: previousState.foo + delta };
    };
}
this.setState(incrementFooBy(2));

Here, we use currying to “compose” what the update should be, based on the previous state and the intended changes and then pass the value to this.setState.

Does it apply to Hooks?

This is probably a question you want to ask so let me stop here and say YES it does. I see this case a lot with the introduction of Hooks. Consider this example below:

const [value, updateValue] = useState({});
const [anotherValue, updateAnotherValue] = useState({});

updateValue({ content: "Hello" }); updateAnotherValue({ content: "World" });

Sometimes when you use Hooks you might find yourself creating multiple state objects in a single functional component. If you are doing this, it could be a sign that your functional component is violating the Single Responsibility Principle, doing more than one thing. If the multiple state objects make sense to belong together, then you should combine the values into one state object like so:

const [allValues, updateAllValues] = useState({});

updateAllValues({firstContent: "Hello", secondContent: "World" });

Or separate the two state objects into their own independent functional component if they don’t make sense to be together. If you don’t fall into any of the above mentioned cases, then I think you should use the unstable_batchedUpdates*.

I feel the need to mention that making multiple calls to update the state isn’t so bad especially because React automatically batches the updates in some cases and in other cases it doesn’t really create any performance issues. So if you find yourself needing to use unstable_batchedUpdates* then you must be in a very rare situation.

Will it be deprecated?

According to Dan Abramov’s response to a Stackoverflow question:

“However, we won’t remove [unstable_batchedUpdates] in a minor version, so you can safely rely on it until React 17 if you need to force batching in some cases outside of React event handlers.”

And another comment he made on a Github issue goes:

“This is expected behavior because we currently only batch updates inside scopes known to React (e.g. during a synchronous lifecycle method, or during an event handler). You can work around this with unstable_batchedUpdates as mentioned above. In the future batching will be on by default everywhere.”

As of the time of writing this article, there’s no mention in the official roadmap blog posts of any React version where the unstable_batchedUpdates will be deprecated and there isn’t much information besides Dan’s comments that more accurately say when the API will be deprecated.

Conclusion

In this post, we have taken a closer look at the batched updates feature and demonstrated how it simplifies the statement management and rendering process in React applications. Having mentioned that this featured is not stable at the moment, it is worthy to note that it can be used at the moment. To get started, simply put your state calls in a callback function passed to ReactDOM.unstablebatchedUpdates*.

Hope this tutorial will surely help and you! Thanks For Visiting, Keep Visiting.

Originally published on blog.logrocket.com

reactjs react-native javascript web-development

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How native is React Native? | React Native vs Native App Development

Article covers: How native is react native?, React Native vs (Ionic, Cordova), Similarities and difference between React Native and Native App Development.

Hire Dedicated React Native Developer

Have you ever thought of having your own app that runs smoothly over multiple platforms? React Native is an open-source cross-platform mobile application framework which is a great option to create mobile apps for both Android and iOS. **[Hire...

Hire Dedicated React Native Developer in India | React Native Development

Hire dedicated React Native developers for your next project. As the top react native development company we offer you the best service as per your business needs.

Which is the best React Native app development company in New York?

Hire top react native app development company in New York to build and develop custom react native mobile apps for Android & iOS with the latest features.

Top React Native Mobile App Development Companies in USA

Looking for top React Native mobile app development company in USA for Startups & Enterprise? Find out the top list of React Native mobile app development company in USA.