React 18 New Hooks ⚡

React 18 New Hooks ⚡

useId , useTransition , useSyncExternalStore

React 18 has introduced 5 new hooks :

React 18 New Hooks

  • useId
  • useTransition
  • useSyncExternalStore
  • UseDeferredValue
  • useInsertionEffect

In this blog we will be covering 3 new hooks:

  • useId
  • useTransition
  • useSyncExternalStore

1. useId

  • UseId is a hook that is used to generate unique Ids.
  • UseId is evolved from useOpaqueIdentifie, which generates an opaque id that cannot be operated upon.
  • We can also add prefix/suffix to the Id.

In any web application, there are many cases where we need unique Ids

for example:

  • <label for="ID">, where the for attribute must be equal to the id attribute of the related element to bind them together.
function NameFields() {
  const id = useId();
  return (
    <div>
      <label htmlFor={id + '-firstName'}>First Name</label>
      <div>
        <input id={id + '-firstName'} type="text" />
      </div>
      <label htmlFor={id + '-lastName'}>Last Name</label>
      <div>
        <input id={id + '-lastName'} type="text" />
      </div>
    </div>
  );
}

This code generated unique Ids for everyElement

As the Ids generated by useIds are globally unique and the suffix/preffix we add will be locally unique which will make the ids globally unique so it will not cause any hydration mismatch during server-side rendering

2. useTransition

  • This hook is used for transition. It returns the transition state and function to start the transition.

By default, all updates in React are considered urgent. That could create a problem when quick updates are slowed down by heavy updates. But in React 18 and the new concurrency features, we can mark some updates as interruptible and non-urgent — so-called transitions. That's especially useful with heavy UI updates, like filtering a big list.

With the use of useTransition() hook we can access concurrent mode features inside of the React component.

const [isPending, startTransition] = useTransition();
  • isPending: indicates that the transition is pending
  • startTransition(callback): this allows us to mark any UI updates inside the callback as transitions.
import { useTransition } from 'react';
function MyComponent() {
  const [isPending, startTransition] = useTransition();
  // ...
  const someEventHandler = (event) => {
    startTransition(() => {
      // Mark updates as transitions
      setValue(event.target.value);
    });
  }
  return <HeavyComponent value={value} />;
}

As already mentioned, we can use useTransition() hook to let know React which UI updates are urgent (like updating the input field value), and which are non-urgent transitions (like updating the names list to highlight the query).

Let's see the example below:

First, let's invoke the [isPending, startTransition] = useTransition() hook to get access to startTransition() function. Secondly, let's create a state variable to hold the query state value specifically for the transition.

export function FilterList({ names }) {
  const [query, setQuery] = useState('');
  const [highlight, setHighlight] = useState('');
  const [isPending, startTransition] = useTransition();
  const changeHandler = ({ target: { value } }) => {
    setQuery(value);
    startTransition(() => setHighlight(value));
  };
  return (
    <div>
      <input onChange={changeHandler} value={query} type="text" />
      {names.map((name, i) => (
        <ListItem key={i} name={name} highlight={highlight} />
      ))}
    </div>
  );
}

3.useSyncExternalStore

useSyncExternalStore is a hook recommended for reading and subscribing from external data sources (stores).

Here is the signature of the hook:

const state = useSyncExternalStore(subscribe, getSnapshot[, getServerSnapshot]);
This method accepts three arguments:
  • subscribe: It is a function to register a callback that is called whenever the store changes.
  • getSnapshot: It is function that returns the current value of the store.
  • getServerSnapshot: It is function that returns the snapshot used during server rendering. This is an optional parameter. This method returns the value of the store, state.

We create an example of useSyncExternalStore, which reads the current browser window width and displays it on the screen.

import { useSyncExternalStore } from 'react';

function App() {
  const width = useSyncExternalStore(
    (listener) => {
      window.addEventListener('resize', listener);
      return () => {
        window.removeEventListener('resize', listener);
      };
    },
    () => window.innerWidth
    // () => -1,
  );

  return <p>Size: {width}</p>;
}

export default App;

The above application calls useSyncExternalStore:

  • subscribe (lines 5–10): It registers a callback for the window resize event listener.
  • getSnapshot (line 11): It returns the current browser window width.
  • getServerSnapshot (line 12): It is for server rendering, which is not needed here, or simply returns -1. Execute the code by npm start. The following video shows that the UI displays the browser window width while resizing.