Frontend Development 22 min read

React 18 New Features, Upgrade Guide, and API Changes

This article provides a comprehensive overview of React 18, covering its release timeline, migration steps, new APIs such as the root API, useId, useSyncExternalStore and useInsertionEffect, automatic state batching, flushSync, updated Strict Mode and Suspense behavior, concurrent mode concepts, and practical code examples for developers upgrading from React 17.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
React 18 New Features, Upgrade Guide, and API Changes

Preface

In June 2021 the React 18 Working Group (reactwg) was established and announced a release plan for version 18. After nearly a year of iteration, React 18 was officially released on March 29 2022.

The official release notes show that React 17 was published on October 20 2020, a year and a half before React 18, and that React 17 only had three patch versions: 17.0.0, 17.0.1, and 17.0.2.

Both minor updates in React 17 only changed patch numbers and performed small fixes, while React 18 introduced many new features. The article explores these features from a developer's perspective.

Notice

React 18 drops support for IE 11; support will be removed on June 15 2022. Projects that still need IE 11 compatibility must stay on React 17.

React 18 introduces new features that rely on modern browser capabilities and cannot be fully polyfilled for IE.

Upgrade

New projects: install the latest dependencies with npm or yarn (TypeScript projects also need the corresponding @types packages).

Existing projects: update version numbers in package.json , delete node_modules , and reinstall.

npm i react react-dom --save
npm i @types/react @types/react-dom -D
npm i

New Features

1. Render API

React 18 adds a new root API that enables the concurrent renderer. The old ReactDOM.render call is replaced by ReactDOM.createRoot(root).render(<App />) . Unmounting now uses root.unmount() instead of ReactDOM.unmountComponentAtNode . The render callback is removed; side‑effects should be placed in useEffect .

// React 17
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const root = document.getElementById('root');
ReactDOM.render(
, root);

// React 18
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = document.getElementById('root');
ReactDOM.createRoot(root).render(
);

When using server‑side rendering, replace ReactDOM.hydrate with ReactDOM.hydrateRoot :

// React 17
import ReactDOM from 'react-dom';
const root = document.getElementById('root');
ReactDOM.hydrate(
, root);

// React 18
import ReactDOM from 'react-dom/client';
const root = document.getElementById('root');
ReactDOM.hydrateRoot(root,
);

TypeScript definitions now require an explicit children? property in component props.

// React 17
interface MyButtonProps { color: string; }
const MyButton: React.FC
= ({ children }) =>
{children}
;

// React 18
interface MyButtonProps { color: string; children?: React.ReactNode; }
const MyButton: React.FC
= ({ children }) =>
{children}
;

2. Automatic State Batching

React 18 performs automatic batching for all state updates, reducing the number of renders. In React 17 only updates inside React event handlers were batched; updates inside setTimeout , native events, or promises caused separate renders.

Examples show the difference: clicking a button that updates two states inside a React event results in a single render in both versions, but the same code inside setTimeout or a native event renders twice in React 17 and only once in React 18.

Developers can still force separate renders using flushSync :

import { flushSync } from 'react-dom';
const App = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  return (
{
      flushSync(() => setCount1(c => c + 1));
      flushSync(() => setCount2(c => c + 1));
    }}>
{count1}
{count2}
);
};

3. Component Unmount Warning

React 18 removes the warning about updating state on an unmounted component, which in earlier versions could be triggered by async effects or timers. The change reduces misleading messages.

4. Component Return Values

In React 17 returning undefined from a component caused a runtime error; React 18 relaxes this check, allowing both null and undefined (type definitions still prefer null ).

5. Strict Mode

Strict Mode now logs both renders but displays the second render in gray via React DevTools, making double‑render debugging less noisy.

6. Suspense Fallback Changes

React 18 no longer skips Suspense boundaries with missing or null fallback. Instead, the boundary renders null as the fallback, preventing silent skips.

New APIs

1. useId

const id = useId();

Generates a stable unique ID that matches between server and client, solving hydration mismatches.

2. useSyncExternalStore

This hook replaces useMutableSource and provides a safe way for external stores (e.g., Redux) to subscribe to updates without tearing under concurrent rendering.

3. useInsertionEffect

const useCSS = rule => {
  useInsertionEffect(() => {
    if (!isInserted.has(rule)) {
      isInserted.add(rule);
      document.head.appendChild(getStyleForRule(rule));
    }
  });
  return rule;
};

const App = () => {
  const className = useCSS(rule);
  return
;
};

Designed for CSS‑in‑JS libraries; it runs after DOM nodes are created but before layout effects.

Concurrent Mode

Concurrent Mode (CM) enables interruptible rendering, turning synchronous, non‑interruptible updates into asynchronous, interruptible ones. In React 18 the root API automatically enables the new Fiber reconciler, and developers can opt‑in to concurrent updates via APIs such as startTransition , useTransition , and useDeferredValue .

stateDiagram-v2
[*] --> React18
React18 --> ReactDOM.render
React18 --> ReactDOM.createRoot
ReactDOM.render --> NotConcurrent
ReactDOM.createRoot --> Concurrent
NotConcurrent --> NoAutoBatch
Concurrent --> AutoBatch
NoAutoBatch --> NoConcurrentUpdates
AutoBatch --> NoConcurrentFeatures
AutoBatch --> UseConcurrentFeatures
NoConcurrentFeatures --> NoConcurrentUpdates
UseConcurrentFeatures --> EnableConcurrentUpdates

1. startTransition

import { useState, useEffect, useTransition } from 'react';
const App = () => {
  const [list, setList] = useState([]);
  const [isPending, startTransition] = useTransition();
  useEffect(() => {
    startTransition(() => setList(new Array(10000).fill(null)));
  }, []);
  return <>{list.map((_, i) =>
{i}
)};
};

startTransition marks state updates as low‑priority, allowing the browser to handle high‑priority work first.

2. useDeferredValue

import { useState, useEffect, useDeferredValue } from 'react';
const App = () => {
  const [list, setList] = useState([]);
  useEffect(() => setList(new Array(10000).fill(null)), []);
  const deferredList = useDeferredValue(list);
  return <>{deferredList.map((_, i) =>
{i}
)};
};

Provides a delayed version of a state value that only updates when no urgent renders are pending.

3. Normal (non‑concurrent) Rendering

import { useState, useEffect } from 'react';
const App = () => {
  const [list, setList] = useState([]);
  useEffect(() => setList(new Array(10000).fill(null)), []);
  return <>{list.map((_, i) =>
{i}
)};
};

Without concurrent features, rendering 10 000 elements blocks the main thread for about 500 ms.

Conclusion

Concurrent updates interleave tasks, yielding to the browser when time is short.

Concurrent Mode is the prerequisite for concurrent updates.

Time‑slicing is the concrete technique that implements concurrent updates.

References

React v18.0

How to Upgrade to React 18

React 18 Working Group

React 18 no longer relies on Concurrent Mode to enable concurrent updates

frontendReactHooksUpgradeConcurrent ModeReact 18
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.