Introduction to React-18 -2

Sean Hsieh-yoyo
4 min readAug 2, 2022

Brief Intro:

According to past share, we had lost three of new hooks. And Today we’re going to talk about these three new hooks

  1. useDeferredValue
  2. useSyncExternalStore
  3. useInsertionEffect

useDeferredValue

Brief: lets you defer updating the less important parts of the screen

const deferredValue = useDeferredValue(value);

useDeferredValue accepts a value and returns a new copy of the value that will defer to more urgent updates.

if the current render is the result of an urgent update, like user input, React will return the previous value and then render the new value after urgent render has completed.

This hook is similar to user-space hooks which use debouncing or throttling to defer updates.

Difference between useTransition:

useTransitionconst [isPending, startTransition] = useTransition();useDeferredconst deferredValue = useDeferredValue(value);

useTransition gives you control if you decide which code should be wrapped and treated as a low non-urgent update.

Sometimes, you might need to access to the actual state to updating code (e.g. state may from some third-party ). Or, some other reason.

Result:

Both hooks have same behavior. The difference is

  • startTransition is used when triggering an update (i.e. setState ) in an event handler
  • useDeferredValue is used when receiving new data from a parent component (or an earlier hook in the same component)

Example

function ProductList({ products }) {
const deferredProducts = useDeferredValue(products);
return (
<ul>
{deferredProducts.map((product) => (
<li>{product}</li>
))}
</ul>
);
}

The benefits to using useDeferredValue is that React will work on the update as soon as other work finishes, deferred values can suspend without triggering an unexpected fallback for existing content.

Memoizing deferred children

useDeferredValue only defers the value that you pass to it.

If you want to prevent a child component from re-rendering during an urgent update, you must also memoize that component with React.memo or React.useMemo

function Typeahead() {
const query = useSearchQuery('');
const deferredQuery = useDeferredValue(query);

// Memoizing tells React to only re-render when deferredQuery changes,
// not when query changes.
const suggestions = useMemo(() =>
<SearchSuggestions query={deferredQuery} />,
[deferredQuery]
);

return (
<>
<SearchInput query={query} />
<Suspense fallback="Loading results...">
{suggestions}
</Suspense>
</>
);
}

Memoizing the children tells React that it only needs to re-render them when deferredQuery changes and not when query changes.

This caveat is not unique to useDeferredValue, and it’s the same pattern you would use with similar hooks that use debouncing or throttling.

Library Hooks:

below hooks are not developed by react first, but react integrate these. And are not typically used in application code.

useSyncExternalStore

Brief:

Since JS is single threaded, this issue generally has not come up in web development. But In React 18, concurrent rendering makes this issue possible because React yields during rendering.

This means that when using a concurrent feature like `startTransition` or `Suspense`, React can pause to let other work happen. Between these pauses, updates can sneak in that change the data being used to render, which can cause the UI to show two different values for same data.

And this is what Tearing mean.

Before React 18:

After React 18:

const state = useSyncExternalStore(subscribe, getSnapshot[, getServerSnapshot]);

useSyncExternalStore is a hook recommended for reading and subscribing from external data sources in a way that’s compatible with concurrent rendering feature like selective hydration and time slicing.

This method returns the value of the store and accepts three arguments:

  • subscribe: function to register a callback that is called whenever the store changes.
  • getSnapshot: function that returns the current value of the store.
  • getServerSnapshot: function that returns the snapshot used during server rendering.

The most basic example simply subscribes to the entire store:

const state = useSyncExternalStore(store.subscribe, store.getSnapshot);

However, you can also subscribe to a specific field:

const selectedField = useSyncExternalStore(store.subscribe, () => store.getSnapshot().selectedField)

useInsertionEffect

useInsertionEffect(didUpdate);

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

Use this to inject styles into the DOM before reading layout in useLayoutEffect.

Since this hook is limited in scope, this hook does not have access to refs and cannot schedule updates.

This means that it can only be to insert styling rules. Its main use case is to insert global DOM nodes like `styles`, or SVGs <defs>.

As this is only relevant for generating tags client-side, the Hook doesn’t run on the server.

NOTE: Make sure you read the section “When to Insert <style> on The Client". If you currently inject style rules "during render", it could make your library VERY slow in concurrent rendering.

ref: https://academind.com/tutorials/react-usetransition-vs-usedeferredvalue

ref: https://reactjs.org/docs/hooks-reference.html#usedeferredvalue

ref: https://github.com/reactwg/react-18/discussions/69

ref: https://github.com/reactwg/react-18/discussions/110

--

--