Skip to content

useMemo, useCallBack, useTransition, useDeferredValue

Here are some common React Hooks to make React more performant.

useMemo

useMemo memoizes the result of a function call and is useful to reduce expensive calculations performed during render. useMemo re-runs the function only when the dependencies change.

js
function Table() {
	const sortedData = useMemo(() => sortData(data, sortBy), [data, sortBy])
	// ....
}

In the above example, the sortData will only re-calculate if the data or sortBy dependencies change.

useMemo is often combined with React.memo to reduce re-rendering. In the example below, the <Data /> component will re-rendering every time <Table /> re-renders because the sortedData will be re-calculated and different on every <Table /> render.

js
function Table({ data, color }) {
	const sortedData = sortData(data, sortBy);
	return <Data sortedData={sortedData}/>;
}

const Data = memo(function Data({sortedData})) {
	// ...
}

If we add useMemo to the sortData then the <Data /> component will be properly memoized and won't re-render when color changes:

js
function Table({ data, color }) {
	const sortedData = useMemo(() => sortData(data, sortBy), [data, sortBy])
	return <Data sortedData={sortedData}/>;
}

useCallback

useCallback is very similar to useMemo, but instead of caching a value, it caches a function. It also takes a list of dependencies (state, props, other variables) as the second arguments to indicate when the function should be updated.

In the example below, if we don't use useCallback the memo on MemoizedComponent will not work, leading to unnecessary re-rendering.

js
function Table() {
	const handleClick = useCallback((value) => {
		updateTable(tableId, value)
	}, [tableId]);
	return (
		<div className="table">
			<MemoizedComponent onClick={handleClick} />
		</div>
	);
}

const MemoizedComponent = memo(function MemoizedComponent({ onClick }) {
	// ...
});

useTransition

useTransition is a hook that prevents blocking the UI during a state change.

js
const [isPending, startTransition] = useTransition()

isPending: a flag to indicate if the transition is pending. startTransition: a function to mark a state update as a transition.

In the below example, the UI won't block when changing theme. Especially useful if LightThemeSlow or DarkThemeSlow are components that take awhile to render.

js
import React, { useState, useTransition } from 'react';

function ThemeSwitcher() {
  const [isPending, startTransition] = useTransition();
  const [theme, setTheme] = useState('light');

  function changeTheme(nextTheme) {
    startTransition(() => {
      setTheme(nextTheme);
    });
  }

  return (
    <div>
      <button onClick={() => changeTheme('light')}>Light Theme</button>
      <button onClick={() => changeTheme('dark')}>Dark Theme</button>
      <div className="content">
        {isPending ? 'Loading...' : 'Content'}
      </div>
      {theme === 'light' && <LightThemeSlow />}
      {theme === 'dark' && <DarkThemeSlow />}
    </div>
  );
}

useDeferredValue

useDeferredValue is a hook to defer updating parts of the UI. This can be used with React.memo to provide a smooth user experience by deferring updating a slow component.

In the example below, the input will stay unblocked as the searchTerm is deferred so that the SlowList will re-render only after the input stop receiving inputs.

js
import { useState, useDeferredValue } from 'react';

export default function List() {
  const [searchTerm, setSearchTerm] = useState('');
  const deferredSearch = useDeferredValue(searchTerm);
  return (
    <>
      <input value={text} onChange={(e) => setSearchTerm(e.target.value)} />
      <SlowList searchTerm={deferredSearch} />
    </>
  );
}