CodeBlanks Logo Icon CodeBlanks

Concurrent features

Make a heavy filter low priority with useTransition.

Concurrent features

In the last step you add concurrent search to the project. The SearchPanel component does the search in the background with useTransition; the input stays smooth.

The starting code includes the previous three solutions. This lesson only adds SearchPanel. So the design system, theme, server page and concurrent search merge in one project.

With isPending a loading indicator is shown during the wait.

Pending with isPending

While isPending is true you show a waiting indicator to the user. This signals that the search is running in the background. The indicator should be short and meaningful; the user must know what they are waiting for. When the search finishes the indicator disappears and the result list appears. This keeps the app looking responsive and the user experience uninterrupted.

Practice Write the SearchPanel component

Write SearchPanel. Import useState and useTransition. Keep query with useState(''), get isPending and startTransition from useTransition. Compute items.filter(i => i.name.includes(query)) as filtered. Show <input value={query} onChange={e => startTransition(() => setQuery(e.target.value))} /> and isPending ? <p>Searching</p> : <ul>{filtered.map(...)}</ul>. Export it.

Starter code

App.jsx
// Button.jsx
function Button({ variant, children }) {
  const className = variant === 'primary' ? 'btn-primary' : 'btn-secondary';
  return <button className={className}>{children}</button>;
}
export default Button;

// ThemeProvider.jsx
import { createContext, useState } from 'react';

const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}
export default ThemeProvider;

// ProductPage.jsx
async function ProductPage() {
  const res = await fetch('/api/products');
  const products = await res.json();
  return (
    <main>
      <h1>Ürünler</h1>
      <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
    </main>
  );
}
export default ProductPage;

// SearchPanel.jsx

// Write the SearchPanel component here

Sample solution

App.jsx
import { useState, useTransition } from 'react';

function SearchPanel({ items }) {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();
  const filtered = items.filter(i => i.name.includes(query));
  return (
    <div>
      <input value={query} onChange={e => startTransition(() => setQuery(e.target.value))} />
      {isPending ? <p>Searching</p> : <ul>{filtered.map(i => <li key={i.id}>{i.name}</li>)}</ul>}
    </div>
  );
}
export default SearchPanel;

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Which hook does SearchPanel use?

  • useTransition Correct answer
  • useRef
  • useReducer
  • useEffect

Explanation: useTransition runs the search in the background.

2 Fill in the blank: start a transition

Question
const [isPending, startTransition] = use____();
Answer
const [isPending, startTransition] = useTransition();

Explanation: useTransition starts a transition.

3 Match concurrent concepts

Term Meaning
useTransition Deferred update
isPending Pending state
startTransition Wraps the update
input Stays smooth

All lessons in Expert