Concurrent Hooks (useTransition, useDeferredValue & useLayoutEffect)

5 questions found

What problem does useTransition solve in a React app?

Intermediate
useTransition lets you mark a state update as not urgent, so React can keep the screen responsive to more important things, like typing in a search box, while a slower update, like filtering a huge list, happens in the background without freezing the page.
function SearchPage() {
  const [isPending, startTransition] = useTransition();
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);

  function handleChange(e) {
    setQuery(e.target.value);
    startTransition(() => {
      setResults(filterHugeList(e.target.value));
    });
  }
  return <input value={query} onChange={handleChange} />;
}
Real-world example A product search page keeps the typing feeling instant even while filtering thousands of products in the background, using useTransition to mark the filtering update as lower priority.

Common follow-ups: What does the isPending value returned by useTransition actually tell you?;How is useTransition different from just using a plain setTimeout to delay an update?

Performance (memo/useMemo);Suspense & Concurrent Rendering

How does useDeferredValue help improve performance when rendering a large list based on user input?

Advanced
useDeferredValue lets you keep showing an older, slightly outdated version of a value while React works on rendering the newer version in the background. This means the input field stays smooth and responsive, while the heavier part of the screen, like a large filtered list, catches up a moment later.
function SearchResults({ query }) {
  const deferredQuery = useDeferredValue(query);
  const results = useMemo(() => filterHugeList(deferredQuery), [deferredQuery]);
  return <ResultsList items={results} />;
}
Real-world example A data table with ten thousand rows stays smooth while a user types quickly into a filter box, since useDeferredValue lets the heavy filtering work catch up slightly behind the user's typing instead of blocking it.

Common follow-ups: How is useDeferredValue different from useTransition, since they solve a similar problem?;How do you show a visual hint to the user that the results are slightly behind what they typed?

Performance (memo/useMemo);Custom Hooks

What is useLayoutEffect and how is it different from the regular useEffect hook?

Intermediate
useLayoutEffect runs right after React updates the screen but before the browser actually paints anything visible to the user. This is useful when you need to measure something on the screen, like an element's size, and make an adjustment before the user ever sees a flicker. Regular useEffect runs a little later, after the browser has already painted.
function Tooltip() {
  const ref = useRef(null);
  const [height, setHeight] = useState(0);

  useLayoutEffect(() => {
    setHeight(ref.current.getBoundingClientRect().height);
  }, []);

  return <div ref={ref}>Tooltip content, height is {height}</div>;
}
Real-world example A tooltip component measures its own height using useLayoutEffect so it can correctly position itself above a button before the user ever sees it flicker or jump into place.

Common follow-ups: Why can using useLayoutEffect too often make an app feel slower than using useEffect?;In what situation would using regular useEffect actually cause a visible flicker?

Effects & Lifecycle;Refs useRef & forwardRef

When should you choose useTransition over useDeferredValue, given they solve a similar kind of problem?

Advanced
Use useTransition when you control the actual state update that is slow, since it lets you wrap that specific update in startTransition. Use useDeferredValue when you receive a value from somewhere else, such as a prop, and cannot wrap the original update yourself, but still want to delay how quickly your own component reacts to that value changing.
// useTransition, you control the update
startTransition(() => setResults(filterList(query)));

// useDeferredValue, you only receive the value as a prop
function List({ query }) {
  const deferredQuery = useDeferredValue(query);
  // ...
}
Real-world example A dashboard component receives a filter value as a prop from its parent and cannot change how the parent updates that value, so it uses useDeferredValue internally instead of useTransition to smooth out its own rendering.

Common follow-ups: Can useTransition and useDeferredValue be used together in the same component?;What actually happens under the hood that makes these updates lower priority?

Performance (memo/useMemo);Suspense & Concurrent Rendering

What is a simple real world example of when useTransition would make an app feel faster?

Beginner
Imagine typing into a search box that filters a list of ten thousand items on every keystroke. Without useTransition, each keystroke might feel slightly delayed because the browser is busy filtering the huge list. Wrapping the filtering update in useTransition tells React that typing feedback is more important, so the input stays smooth while the filtering catches up right after.
const [isPending, startTransition] = useTransition();

function handleSearch(value) {
  setSearchText(value); // updates instantly, feels smooth
  startTransition(() => {
    setFilteredItems(searchItems(value)); // allowed to happen slightly later
  });
}
Real-world example A large contact list app keeps the search box feeling instantly responsive even while searching through thousands of contacts, thanks to marking the search results update as a transition.

Common follow-ups: What visual feedback, like a loading indicator, should you show while isPending is true?;Is useTransition needed for every state update, or only for genuinely slow ones?

Performance (memo/useMemo);Hooks