SuperAPI AI Directory

react-concurrent

Frontend & websites

View repository

Updated 10-15 18:52

About this skill

React Concurrent Features

Full Reference: See advanced.md for advanced concurrent patterns including priority-based updates, optimistic updates with transitions, router integration, form actions, and debugging techniques.

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: react, topic: concurrent for comprehensive documentation.

When NOT to Use This Skill

Skip this skill when:

  • Using React 17 or earlier (not available)
  • Building simple apps with fast renders
  • All state updates are already fast (< 16ms)
  • Working with non-React frameworks
  • Server Components handle the work (Next.js App Router)

Overview

Concurrent React allows interrupting renders to keep the UI responsive:

Traditional Rendering:
[Update starts]────────────────────────[Render complete]
                    UI blocked!

Concurrent Rendering:
[Update starts]──[pause]──[higher priority]──[resume]──[complete]
                    UI remains responsive!

useTransition

Mark state updates as non-urgent (can be interrupted):

import { useState, useTransition } from 'react';

function TabContainer() {
  const [tab, setTab] = useState('home');
  const [isPending, startTransition] = useTransition();

  function selectTab(nextTab: string) {
    startTransition(() => {
      setTab(nextTab);
    });
  }

  return (
    <div>
      <nav>
        {['home', 'about', 'contact'].map((t) => (
          <button key={t} onClick={() => selectTab(t)}>
            {t}
          </button>
        ))}
      </nav>

      <div className={isPending ? 'opacity-50' : ''}>
        {tab === 'home' && <Home />}
        {tab === 'about' && <About />}
        {tab === 'contact' && <Contact />}
      </div>
    </div>
  );
}

useDeferredValue

Defer updating a value to keep UI responsive:

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

function SearchPage() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  const isStale = query !== deferredQuery;

  return (
    <div>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />

      <div className={isStale ? 'opacity-50' : ''}>
        <SearchResults query={deferredQuery} />
      </div>
    </div>
  );
}

// Memoize to actually benefit from deferred value
const SearchResults = memo(function SearchResults({ query }: { query: string }) {
  const results = useMemo(() => {
    return items.filter(item =>
      item.name.toLowerCase().includes(query.toLowerCase())
    );
  }, [query]);

  return <ul>{results.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
});

startTransition (without Hook)

For use outside components or when you don't need isPending:

import { startTransition } from 'react';

function handleClick() {
  startTransition(() => {
    setPage('/heavy-page');
  });
}

Suspense with Transitions

function App() {
  const [tab, setTab] = useState('home');
  const [isPending, startTransition] = useTransition();

  return (
    <div>
      <nav>
        <button onClick={() => startTransition(() => setTab('posts'))}>
          Posts
        </button>
      </nav>

      <Suspense fallback={<TabSkeleton />}>
        <div className={isPending ? 'opacity-50' : ''}>
          {tab === 'posts' && <Posts />}
        </div>
      </Suspense>
    </div>
  );
}

useTransition vs useDeferredValue

Feature useTransition useDeferredValue
Purpose Wrap state updates Defer a value
Usage When you control the update When you receive a value
Returns [isPending, startTransition] Deferred value
Use case Button clicks, form submits Props from parent

Anti-Patterns

Anti-Pattern Why It's Bad Correct Approach
Wrapping every state update Adds unnecessary complexity Only for heavy updates
Not memoizing child components No benefit from deferred value Use memo()
Using for fast operations Performance overhead Reserve for slow renders (> 50ms)
Ignoring isPending state User sees no feedback Show loading indicator
Multiple transitions unnecessarily Confusing behavior Batch related updates

Quick Troubleshooting

Issue Likely Cause Solution
No performance improvement Child not memoized Add memo() to component
Still blocking UI Synchronous heavy computation Move to Web Worker
isPending always false Update completes too fast No transition needed
Stale UI shown too long No loading indicator Check and display isPending
Transition not working Using in React < 18 Upgrade to React 18+

Best Practices

  • ✅ Use for user-initiated heavy updates
  • ✅ Combine with memo() for list components
  • ✅ Show visual feedback during pending state
  • ✅ Use for route transitions
  • ✅ Prefer useDeferredValue for received props
  • ❌ Don't wrap every state update
  • ❌ Don't use for fast operations
  • ❌ Don't forget to memoize child components

Reference Documentation

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: react, topic: concurrent for comprehensive documentation.