返回 Skill 列表
extension
分类: 开发与工程无需 API Key

react-concurrent

React并发特性用于响应式UI。涵盖useTransition、useDeferredValue、Suspense、startTransition、并发渲染模式和基于优先级的更新。使用时机:用户提到“useTransition”、“useDeferredValue”、“startTransition”、“并发渲染”、“非紧急更新”,或询问关于“保持UI响应性”、“React中的优先级更新”。不适用情况:React 17及更早版本(无并发模式)、无需重度渲染的简单应用程序、非React框架

person作者: jakexiaohubgithub

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.