---
title: "Why React 18 Broke Your App | CoderPad"
description: "React 18's internal changes improved a lot, but may have broken your app in the process. Here's why and how you can fix it"
canonical: "https://coderpad.io/blog/development/why-react-18-broke-your-app/"
last_modified: "2023-06-07 14:12:40"
---
[Back to blog](https://coderpad.io/blog/)

# Why React 18 Broke Your App

January 27, 2022 5 Minute Read

By Corbin Crutchley

[Development](https://coderpad.io/blog/development/)

You’ve just gotten done with [your React 18 upgrade](https://coderpad.io/blog/development/how-to-upgrade-to-react-18/), and, after some light QA testing, don’t find anything. “An easy upgrade,” you think.

Unfortunately, down the road, you receive some internal bug reports from other developers that make it sound like your debounce hook isn’t working quite right. You decide to make a minimal reproduction and create a demo of said hook.
You expect it to throw an “alert” dialog after a second of waiting, but weirdly, the dialog never runs at all.

This is strange because it was working just last week on your machine! Why did this happen? What changed?

The reason your app broke in React 18 is that you’re using`StrictMode`.

Simply go into your`index.js`(or`index.ts`) file, and change this bit of code:

```
render(
  <StrictMode>
    <App />
  </StrictMode>
);Code language: JavaScript (javascript)
```

To read like this:

```
render(
    <App />
);Code language: JavaScript (javascript)
```

All of the bugs that were seemingly introduced within your app in React 18 are suddenly gone.

Only one problem: These bugs are real and existed in your codebase before React 18 – you just didn’t realize it.

## Learn how to run front-end developer interviews that don’t suck

[Read our guide](https://coderpad.io/blog/interviewing/5-tips-for-interviewing-frontend/)

## Proof of broken component

Looking at our example from before, we’re using [React 18’s`createRoot`API](https://coderpad.io/blog/development/how-to-upgrade-to-react-18/) to render our`App`inside of a`StrictMode`wrapper in lines 56 – 60.

Currently, when you press the button, it doesn’t do anything. However, if you remove the

`StrictMode`and reload the page, you can see an`Alert`after a second of being debounced.

Looking through the code, let’s add some`console.log`s into our`useDebounce`, since that’s where our function is supposed to be called.

```
function useDebounce(cb, delay) {
  const inputsRef = React.useRef({ cb, delay });
  const isMounted = useIsMounted();
  React.useEffect(() => {
    inputsRef.current = { cb, delay };
  });
  return React.useCallback(
    _.debounce((...args) => {
        console.log("Before function is called", {inputsRef, delay, isMounted: isMounted()});
          if (inputsRef.current.delay === delay && isMounted())
                      console.log("After function is called");
                  inputsRef.current.cb(...args);
        }, delay),
    [delay]
  );
}Code language: JavaScript (javascript)
```

>

`Before function is called Object { inputsRef: {…}, delay: 1000, isMounted: false }`

Oh! It seems like`isMounted`is never being set to true, and therefore the`inputsRef.current`callback is not being called: that’s our function we wanted to be debounced.

Let’s take a look at the`useIsMounted()`codebase:

```
function useIsMounted() {
  const isMountedRef = React.useRef(true);
  React.useEffect(() => {
    return () => {
          isMountedRef.current = false;
    };
  }, []);
  return () => isMountedRef.current;
}Code language: JavaScript (javascript)
```

This code, at first glance, makes sense. After all, while we’re doing a cleanup in the return function of`useEffect`to remove it at first render, useRef`’s initial setter runs at the start of each render, right?

Well, not quite.

## What changed in React 18?

In older versions of React, you would mount a component once and that would be it. As a result, the initial value of`useRef`and`useState`could almost be treated as if they were set once and then forgotten about.

In React 18, the React developer team decided to change this behavior and [re-mount each component more than once in strict mode](https://github.com/reactwg/react-18/discussions/19). This is in strong part due to the fact that a potential future React feature will have exactly that behavior.

See, one of the features that the React team is hoping to add in a future release utilizes a concept of “ [reusable state](https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state) ”. The general idea behind reusable state is such that if you have a tab that’s un-mounted (say when the user tabs away), then re-mounted (when the user tabs back), React will recover the data that was assigned to said tab component. This data being immediately available allows you to render the respective component immediately without hesitation.

Because of this, while data inside of, say,`useState`may be persisted, it’s imperative that effects are properly cleaned up and handled properly. [To quote the React docs](https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state):

>

This feature will give React better performance out-of-the-box but requires components to be resilient to effects being mounted and destroyed multiple times.

However, this behavior shift in Strict Mode within React 18 isn’t just protective future-proofing from the React team: it’s also a reminder to follow React’s rules properly and to clean up your actions as expected.

After all, the [React team themselves have been warning that an empty dependent array](https://reactjs.org/docs/hooks-reference.html#usememo) (`[]`as the second argument) should not guarantee that it only runs once for ages now.

In fact, this article may be a bit of a misnomer – [the React team says they’ve upgraded thousands of components in Facebook’s core codebase without significant issues](https://github.com/reactwg/react-18/discussions/19#discussioncomment-796197=). More than likely, a majority of applications out there will be able to upgrade to the newest version of React without any problems.

All that said, these React missteps crawl their way into our applications regardless. While the React team may not anticipate many breaking apps, these errors seem relatively common enough to warrant an explanation.

## How to fix the remounting bug

The code I linked before was written by me in a production application and it’s wrong. Instead of relying on`useRef`to initialize the value once, we need to ensure the initialization runs on every instance of`useEffect`

```
function useIsMounted() {
  const isMountedRef = React.useRef(true);
  React.useEffect(() => {
  isMountedRef.current = true; // Added this line  
  return () => {
          isMountedRef.current = false;
    };
  }, []);
  return () => isMountedRef.current;
}
Code language: JavaScript (javascript)
```

This is true for the inverse as well! We need to make sure to run cleanup on any components that we may have forgotten about before.
Many ignore this rule for`App`and other root elements that they don’t intend to re-mount, but with new strict mode behaviors, that guarantee is no longer a safe bet.

To solve this application across your app, look for the following signs:

- Side effects with cleanup but no setup (like our example)
- A side effect without proper cleanup
- Utilizing`[]`in`useMemo`and`useEffect`to assume that said code will only run once

One this code is eliminated, you should be back to a fully functioning application and can re-enable StrictMode in your application!

## Conclusion

React 18 brings many amazing features to the table, such as [new suspense features](https://reactjs.org/docs/concurrent-mode-suspense.html), [the new useId hook](https://github.com/reactwg/react-18/discussions/111), [automatic batching](https://github.com/reactwg/react-18/discussions/21), and more. While refactor work to support these features may be frustrating at times, it’s important to remember that they a serve real-world benefit to the user.

For example, React 18 also introduces some functionality to debounce renders in order to create a much nicer experience when rapid user input needs to be processed.

For more on the React 18 upgrade process, take a look at [our instruction guide on how to upgrade to React 18](https://coderpad.io/blog/development/how-to-upgrade-to-react-18/)

## Subscribe for more CoderPad insights

## Trending Articles

Previous slide Next slide [Development

## What is Prompt Engineering?

Introducing AI tools such as ChatGPT and Bard helps increase the efficiency of tasks such as data generation, code generation and completion, text summarization, etc. How were these AI tools trained to answer almost every question you ask them? The answer, prompt engineering, is discussed in this blog post.

Read the post](https://coderpad.io/blog/development/what-is-prompt-engineering/) [Development

## A Guide To Using React’s useCallback() Hook

Are you looking to optimize your React applications? Have you heard of the useCallback hook? useCallback is a powerful React hook that helps you optimize your application by preventing unnecessary re-renders. In this post, we'll dive into React's useCallback() hook, define referential equality and callback functions and how to tie it all together.

Read the post](https://coderpad.io/blog/development/a-guide-to-using-reacts-usecallback-hook/) [Development

## MySQL vs. PostgreSQL: How Do They Compare?

A database is a convenient tool that allows users to access information, maintain records, and apply commonly used operations, such as insertion, modification, deletion, and data organization. Learn about MySQL vs PostgreSQL, their features, advantages, and disadvantages. Finally, check out which one is the best choice for you.

Read the post](https://coderpad.io/blog/development/mysql-vs-postgresql-how-do-they-compare/) [Development

## PostgreSQL LIKE Operator: A Detailed Guide

When using databases, searching for values that match a given pattern is a familiar necessity. Learn how you can retrieve entries with queries including a particular string using Postgres LIKE operator.

Read the post](https://coderpad.io/blog/development/postgresql-like-operator-a-detailed-guide/) [Development

## How To Use DataProvider In TestNG With Examples

When it comes to testing there are many tools and techniques available. In this post, we'll discuss how to use DataProvider with TestNG.

Read the post](https://coderpad.io/blog/development/how-to-use-dataprovider-in-testng-with-examples/) [Development

## How To Get Up And Running With Django Migrations: A Guide

Django migrations are a way of handling the application of changes to a database schema. By following the steps outlined in this guide, you can easily set up and use migrations in your Django projects.

Read the post](https://coderpad.io/blog/development/how-to-get-up-and-running-with-django-migrations-a-guide/) [Development

## Writing A Parameterized Test In JUnit With Examples

A JUnit parameterized test allows you to decouple data from structure, generating cleaner and more reliable tests. Learn more in this guide.

Read the post](https://coderpad.io/blog/development/writing-a-parameterized-test-in-junit-with-examples/) [Development

## How To Use Assert In JUnit With Examples

The JUnit Assert class is what you use to write unit testing assertions in JUnit. Learn more about it in this guide, full of examples.

Read the post](https://coderpad.io/blog/development/how-to-use-assert-in-junit-with-examples/) [Development

## A Simple Guide To TypeORM: What It Is And How To Use It

Considering current languages have object-oriented programming features, most database management systems are based on the relational model, with tables instead of objects. For these reasons, mapping relational objects is very important in development. In this article, you will learn what TypeORM, one of the main tools used today, is.

Read the post](https://coderpad.io/blog/development/a-simple-guide-to-typeorm-what-it-is-and-how-to-use-it/) [Development

## Using PreparedStatement in JDBC With Examples

In this post, we'll see why and how to work with JDBC and PreparedStatement. We will also learn along with examples using code.

Read the post](https://coderpad.io/blog/development/using-preparedstatement-in-jdbc-with-examples/) [Development

## React vs React Native: The Differences Explained

React and React Native are similar in that they both allow web developers to build applications based on the unique concept of components. But the two frameworks do this in radically different ways. In this post, we cover the key differences between react and react native frameworks, implementation, and which is an appropriate choice.

Read the post](https://coderpad.io/blog/development/react-vs-react-native-the-differences-explained/) [Development

## Logging in Django

The most challenging part of fixing a problem is identifying it. Unfortunately, this statement is exceptionally true when it comes to debugging. Learn how to use the power of logging into your Django applications to improve your debug process in this article.

Read the post](https://coderpad.io/blog/development/logging-in-django/)

## Hire with confidence.

See why engineering teams trust CoderPad to evaluate developers with realistic, AI-ready hiring workflows.

[Request a demo](https://coderpad.io/sales/)
