Why did useEffect replace In React 18?

Why did useEffect replace In React 18?

The useEffect Hook allows us to replace repetitive component lifecycle code. Essentially, a Hook is a special function that allows you to “hook into” React features. Hooks are a great solution if you've previously written a functional component and realize that you need to add a state to it.

useInsertionEffect : useEffect(didUpdate) accepts a function that contains imperative, possibly effectful code, which are mutations, subscriptions, timers, logging, and other side effects. By default, effects run after every completed render, but the invocation can be controlled with a second argument of an array.

useLayoutEffect has the same signature as useEffect, but it fires synchronously after all DOM mutations. i.e. it is fired before useEffect. It is used to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

useInsertionEffect is introduced in React 18. It has the same signature as useEffect, but it fires synchronously before all DOM mutations. i.e. it is fired before useLayoutEffect. It is used to inject styles into the DOM before reading layout.

useInsertionEffect is intended for CSS-in-JS libraries, such as styled-components. Since this hook is limited in scope, this hook does not have access to refs and cannot schedule updates.

The following example, placed in src/App.js, compares useEffect, useLayoutEffect, and useInsertionEffect:

import { useEffect, useInsertionEffect, useLayoutEffect } from 'react';

const Child = () => {
  useEffect(() => {
    console.log('useEffect child is called');
  });
  useLayoutEffect(() => {
    console.log('useLayoutEffect child is called');
  });
  useInsertionEffect(() => {
    console.log('useInsertionEffect child is called');
  });
};

function App() {
  useEffect(() => {
    console.log('useEffect app is called');
  });
  useLayoutEffect(() => {
    console.log('useLayoutEffect app is called');
  });
  useInsertionEffect(() => {
    console.log('useInsertionEffect app is called');
  });
  return (
    <div className="App">
      <Child />
      <p>Random Text</p>
    </div>
  );
}

export default App;

The above application has an App (lines 15–31) and a Child component (lines 3–13). Both of them call useEffect, useLayoutEffect, and useInsertionEffect.

Execute the code by npm start, and we see the displaying text and console output.

These effects are called in the following order:

useInsertionEffect child is called. useInsertionEffect app is called. useLayoutEffect child is called. useLayoutEffect app is called. useEffect child is called. useEffect app is called. This is the expected order.

But, why are they called twice?

With the release of React 18, StrictMode gets an additional behavior that is called strict effects mode. When strict effects are enabled, React intentionally double-invokes effects (mount -> unmount -> mount) for newly mounted components in development mode. Interestingly, useInsertionEffect is not called twice.

Execute npm run build, and serve -s build. We can verify that in production mode, this app is only mounted once.

Conclusion :

useInsertionEffect is the hook to be used if we want it to fire before all DOM mutations. However, useInsertionEffect is intended for CSS-in-JS libraries. For normal application developers, useEffect or useLayoutEffect are used more commonly.