# Notes about React 18 RC.0

I am writing this note while React 18 is in release candidate stage. I created this note because I did not closely follow its development progress, and I’m only aware that React 18 will be released soon after the RC was published.

I studied React’s excellent(opens new window) test suite and compared test reports between React 17 and 18-rc.0(opens new window)[1] and gathered what I learned from the tests, relevant blog posts and discussions(opens new window)[2] in this note.

  1. ReactDOMRoot: ReactDOM.createRoot supersedes ReactDOM.render.(opens new window)

    • ReactDOM.render is still available but this creates a “root” in “legacy” mode(opens new window). This API is called “Legacy root API”. A legacy root behaves like React 17.

      // Before (Legacy Root API)
      ReactDOM.render(<App tab="home" />, container)
      
      // After (New Root API)
      const root = ReactDOM.createRoot(container)
      root.render(<App tab="home" />)
      
    • The new API is called the New root API. The new root API enables concurrent rendering.

    • This new API no longer has the ability to specify a render callback.(opens new window) Instead, put a ref callback on the target DOM node to be notified when something is mounted.

      • Due to concurrent rendering, the ref may not be assigned immediately (i.e. synchronously), unless ReactDOM.flushSync is used.
  2. ReactDOMRoot: ReactDOM.hydrateRoot supersedes ReactDOM.hydrate.(opens new window)

    // Before
    ReactDOM.hydrate(<App tab="home" />, container)
    
    // After
    const root = ReactDOM.hydrateRoot(container, <App tab="home" />)
    
  3. ReactDOMFiberAsync: In the New root API, state updates will be batched by default(opens new window).

  4. ReactFlushSync: ReactDOM.flushSync(opens new window) takes a callback that updates React state. It invokes the callback, performs the update, and flushes them to DOM synchronously. Replaces unstable_batchedUpdates(opens new window).

  5. ReactTransition: React.startTransition(opens new window) takes a callback that updates React state and updates it with lower priority (they call it non-urgent updates). See a real world example.(opens new window) See tests.(opens new window)

    setInputValue(input) // urgent (default priority)
    startTransition(() => {
      setSearchQuery(input) // non-urgent (idle priority)
    })
    
  6. ReactTransition: React.useTransition(opens new window) exposes an isPending status for transitions that are started using the returned startTransition callback. This allows the UI to display feedback to the user that the UI is still updating.

    const [isPending, startTransition] = useTransition()
    // Use the returned `startTransition` instead of React.startTransition.
    
  7. ReactDOMUseId: React.useId()(opens new window) generates an ID that remains consistent between server and client rendering.

  8. ReactEmptyComponent: Components may now return undefined in addition to null(opens new window).

  9. ReactDOMFizzServer: [3] React.Suspense can now be rendered on the server(opens new window).

  10. StrictEffectsMode: Under the New Root API, inside <React.StrictMode>, useEffect and useLayoutEffects callbacks are now(opens new window) doubled(opens new window) in dev mode (see tests(opens new window)). That is, given the following code, it will call subscribe(); unsubscribe(); subscribe():

    useEffect(() => {
      subscribe()
      return () => unsubscribe()
    }, [])
    
  11. useSyncExternalStore: state = useSyncExternalStore(subscribe, get)(opens new window) is introduced to allow components “to safely and efficiently read from a mutable external source”(opens new window) in a way that works with concurrent rendering. For older React versions that support hooks, use-sync-external-store/shim(opens new window) package can be used. See tests(opens new window).

  12. ReactHooks: useInsertionEffect is for performing effects before useLayoutEffect(opens new window), e.g. injecting style tags performantly in CSS-in-JS scenario when React is rendering concurrently.


  1. The bold text in front of each entry is an internal name in React source code that you can use to learn more about the feature or change, until official documentation is available. My recomendation is to look up test files with that name, as they very precisely describe the behavior of the API. ↩︎

  2. At the time of writing, I wasn’t aware that React Conf 2021(opens new window) happened, and that React 18 RC was released during the conference. Maybe you can go watch the keynotes instead… ↩︎

  3. “React Fizz”(opens new window) is the codename for new server-rendering architecture, which supports streaming and suspense. ↩︎