Breaking Changes in React 18

createRoot enables concurrent features from React 18.


One of the changes you can make is to alter render to createRoot like so:

// Before
import { render } from "react-dom";

const container = document.getElementById("app");
render(<App tab="home" />, container);

// After
import { createRoot } from "react-dom/client";

const container = document.getElementById("app");
const root = createRoot(container);
root.render(<App tab="home" />);

one render per click in the console

https://codesandbox.io/p/sandbox/morning-sun-lgz88

notice two renders per click in the console

https://codesandbox.io/p/sandbox/jolly-benz-hb1zx

flushSync

If automatic batching is not something you want in your component, you can always opt-out with flushSync. Let's go through an example:

import { flushSync } from "react-dom"; // Note: we are importing from react-dom, not react

function handleSubmit() {
  flushSync(() => {
    setSize((oldSize) => oldSize + 1);
  });

  // React has updated the DOM by now
  flushSync(() => {
    setOpen((oldOpen) => !oldOpen);
  });

  // React has updated the DOM by now
}

useInsertionEffect

The signature of useInsertionEffect is identical to useEffect, but it fires synchronously before all DOM mutations.


This hook is meant to inject styles into the DOM before reading layout in useLayoutEffect.

When to Use useInsertionEffect

  1. Initializing Third-party Libraries: You may want to initialize a third-party library like Chart.js

import React from 'react';
import { useInsertionEffect } from 'react';
import Chart from 'chart.js';

const ChartComponent = () => {
  useInsertionEffect(() => {
    const ctx = document.getElementById('myChart').getContext('2d');
    new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [{
          label: '# of Votes',
          data: [12, 19, 3, 5, 2, 3],
          backgroundColor: [
            'rgba(255, 99, 132, 0.2)',
            'rgba(54, 162, 235, 0.2)',
            'rgba(255, 206, 86, 0.2)',
            'rgba(75, 192, 192, 0.2)',
            'rgba(153, 102, 255, 0.2)',
            'rgba(255, 159, 64, 0.2)'
          ],
          borderColor: [
            'rgba(255, 99, 132, 1)',
            'rgba(54, 162, 235, 1)',
            'rgba(255, 206, 86, 1)',
            'rgba(75, 192, 192, 1)',
            'rgba(153, 102, 255, 1)',
            'rgba(255, 159, 64, 1)'
          ],
          borderWidth: 1
        }]
      },
      options: {
        scales: {
          y: {
            beginAtZero: true
          }
        }
      }
    });
  }, []);
  return <canvas id="myChart" width="400" height="400"></canvas>;
};
export default ChartComponent;
  1. Adding Event Listeners

import React from 'react';
import { useInsertionEffect } from 'react';

const ButtonComponent = () => {
  useInsertionEffect(() => {
    const button = document.getElementById('myButton');
    const handleClick = () => {
      console.log('Button clicked!');
    };
    button.addEventListener('click', handleClick);
    return () => {
      button.removeEventListener('click', handleClick);
    };
  }, []);
  return <button id="myButton">Click me</button>;
};
export default ButtonComponent;
  1. Triggering Animations: You might want to trigger animations or transitions when a component becomes visible on the screen.

Note: useInsertionEffect is meant to be limited to css-in-js library authors. You should instead use useEffect or useLayoutEffect.


CSS-in-JS is a technique for writing CSS styles directly into JavaScript files, allowing developers to define and manage styles in JavaScript. For example: styled-component


Ref: https://medium.com/zestgeek/mastering-reacts-useinsertioneffect-hook-a-comprehensive-guide-with-examples-36761040915d

useInsertionEffect VS useEffect VS useLayoutEffect

useInsertionEffect is executed synchronously before all DOM changes. This means that it will be executed before the browser draws, blocking the browser's drawing.


The useLayoutEffect is executed synchronously after all DOM changes. This means that it is executed before the browser draws, blocking the browser's drawing.


useEffect is executed asynchronously after the browser finishes layout and drawing. This means that it does not block the browser's drawing.

useId

react-router-dom v6 及以上版本中,Route 组件的 component 属性已经被 element 属性取代。你需要将 component 替换为 element 并使用 JSX 语法。

import { Route, BrowserRouter, Routes } from 'react-router-dom';
import UseId from './UseId';
import './App.css';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/useId" element={<UseId />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;
    A        0
   /        \\
  B(00)        C(01)
 /    |         |       \\
D(000) E(001)   F(0010)   G(0011)

**Binary => 32** 

Ref: https://cloud.tencent.com/developer/article/1977997

Hydrate

https://codesandbox.io/p/sandbox/7c4z7w?file=%2Fsrc%2FApp.js

var fieldCounter = 0

export default function Field(props) {
  const [inputId, setInputId] = useState(++fieldCounter)

  return (
    <div className='field'>
      <label htmlFor={inputId}>props.label</label>
      <input type={props.type} id={inputId} name={props.name} value="" />
    </div>
  )
}

useTransition

Urgent updates are the ones that reflect direct interaction, like typing, clicking, pressing, and so on.

Transition updates transition the UI from one view to another in a non-urgent manner.

with useTransition

https://codesandbox.io/p/sandbox/tkyfgs

without useTransition

https://codesandbox.io/p/sandbox/pjdrgh?file=%2Fsrc%2FApp.js

useDeferredValue

  • defer re-rendering a non-urgent part of the tree.

  • no fixed time delay

    • As soon as React finishes the original re-render, React will immediately start working on the background re-render with the new deferred value. Any updates caused by events (like typing) will interrupt the background re-render and get prioritized over it.

  • The deferred render is interruptible and doesn't block user input.

https://codesandbox.io/p/sandbox/7nyzhn?file=%2Fsrc%2FApp.js%3A6%2C24-6%2C40

without useDeferredValue

https://codesandbox.io/p/sandbox/lc57th?file=%2Fsrc%2FApp.js%3A4%2C32

useSyncExternalStore

This hook is intended for library authors and is not typically used in application code.


When possible, we recommend using built-in React state with useState and useReducer instead. The useSyncExternalStore API is mostly useful if you need to integrate with existing non-React code.


reading and subscribing from external data sources

  • Third-party state management libraries that hold state outside of React

  • Browser APIs that expose a mutable value and events to subscribe to its changes

examples:

  • Browser history

  • localStorage

  • Third-party data sources

Update React components when some data in external stores changes without using useEffect.

  1. The subscribe function should subscribe to the store and return a function that unsubscribes.

    1. takes a single callback argument and subscribes it to the store

    2. When the store changes, it should invoke the provided callback, which will cause React to re-call getSnapshot and (if needed) re-render the component.

      1. The only thing we need to do is that the callback needs to be executed when the store changes. callback is passed from within react, and its main role is to execute the internal forceStoreRerender(fiber) method in order to force an update to the UI.

    3. should return a function that cleans up the subscription.

  2. The getSnapshot function should read a snapshot of the data from the store.

  3. optional getServerSnapshot: A function that returns the initial snapshot of the data in the store.

    1. It will be used only during server rendering and during hydration of server-rendered content on the client.

import { useSyncExternalStore } from "react";

type Theme = "light" | "dark";

const THEME_STORAGE_KEY = "app-theme";

const getThemeFromLocalStorage = (): Theme => {
  return (localStorage.getItem(THEME_STORAGE_KEY) as Theme) || "light";
};

const subscribe = (callback: () => void): (() => void) => {
  window.addEventListener("storage", callback);
  return () => {
    window.removeEventListener("storage", callback);
  };
};

const useThemeStore = (): [Theme, (newTheme: Theme) => void] => {
  const theme = useSyncExternalStore(subscribe, getThemeFromLocalStorage);

  const setTheme = (newTheme: Theme) => {
    localStorage.setItem(THEME_STORAGE_KEY, newTheme);
    window.dispatchEvent(new Event("storage"));
  };

  return [theme, setTheme];
};

export default useThemeStore;

ref:


https://www.56kode.com/posts/using-usesyncexternalstore-with-localstorage/


https://developer.aliyun.com/article/1412139


another example: https://codesandbox.io/p/sandbox/react-dev-forked-82w2sh?file=%2Fsrc%2FtodoStore.js%3A30%2C25

Other Notable React 18 Changes

Bye-bye Older Browsers!

React now depends on modern browser features, including PromiseSymbol, and Object.assign.


Consider including a global polyfill in your bundled application if you support older browsers and devices such as Internet Explorer, which do not provide modern browser features natively or have non-compliant implementations.

Components Can Now Render undefined

React no longer throws an error if you return undefined from a component. The allowed component returns values consistent with allowed values in the middle of a component tree. The React team suggests using a linter to prevent mistakes like forgetting a return statemen98t before JSX.

No setState Warning on Unmounted Components

Previously, React warned about memory leaks when you called setState on an unmounted component. This warning was added for subscriptions, but people primarily ran into it in scenarios where the setting state was fine, and workarounds would worsen the code.

Improved Memory Usage

React now cleans up more internal fields on unmount, so the impact from unfixed memory leaks in your application code is less severe. It would be interesting to see how memory usage drops compared to the previous versions.

Last updated: 2026-03-20 13:51:01Scala/HTTP4s/Cats
Author:Chaolocation:https://www.baidu.com/article/35
Comments
Submit
Be the first one to write a comment ~