Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问许可证需确认审计通过

react-render-optimizationReact render optimization 搜索

Agent Skill

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

总安装

8,176

周安装

334

GitHub Stars

173

下载量

2,645
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:react-render-optimization(React render optimization 搜索)
来源仓库:https://github.com/patternsdev/skills
仓库路径:skills/react-render-optimization
安装命令:
npx skills add https://github.com/patternsdev/skills --skill react-render-optimization
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/patternsdev/skills --skill react-render-optimization

简介

深入分析 React 渲染机制,提出减少无效更新的具体优化手段。

  • 适用于父组件频繁更新导致子组件意外重渲染的性能敏感场景。
  • 可使用 React.memo、useMemo、useCallback 等手段精细化控制更新粒度。
  • 需警惕过度 memoization 带来的额外计算开销,按需选择性应用。
  • 结合 Profiler API 量化优化效果,形成数据驱动的决策依据。

SKILL.md

React Render Optimization

Table of Contents

Practical patterns for eliminating unnecessary re-renders, reducing rendering cost, and keeping React UIs responsive. These patterns apply to any React application — whether you're using Vite, Next.js, Remix, or a custom setup.

When to Use

Reference these patterns when:

  • Components re-render more often than expected
  • UI feels sluggish during typing, scrolling, or interactions
  • Profiler shows wasted renders in the component tree
  • Building performance-sensitive features (dashboards, editors, lists)
  • Reviewing or refactoring existing React components

Instructions

  • Apply these patterns during code generation, review, and refactoring. When you see an anti-pattern, suggest the corrected version with an explanation.

Details

Overview

React re-renders a component whenever its state changes, a parent re-renders, or context it consumes updates. Most re-renders are harmless, but when they trigger expensive computation, deep trees, or layout thrashing they become visible to users.

The patterns below are ordered by impact — address the biggest wins first before reaching for micro-optimizations.


1. Compute Derived Values During Render — Don't Store Them

Impact: HIGH — Eliminates an entire category of bugs and unnecessary state.

Storing values that can be computed from existing state or props creates synchronization problems and extra re-renders. Compute them inline instead.

Avoid — redundant state that drifts:

function ProductList({ products }: { products: Product[] }) {
  const [search, setSearch] = useState('')
  const [filtered, setFiltered] = useState(products)

  useEffect(() => {
    setFiltered(products.filter(p =>
      p.name.toLowerCase().includes(search.toLowerCase())
    ))
  }, [products, search])

  return (
    <>
      <input value={search} onChange={e => setSearch(e.target.value)} />
      {filtered.map(p => <ProductCard key={p.id} product={p} />)}
    </>
  )
}

Prefer — derive during render (cheap derivations use plain const):

function ProductList({ products }: { products: Product[] }) {
  const [search, setSearch] = useState('')

  // Cheap derivation — plain const, no useMemo needed
  const hasSearch = search.length > 0
  const normalizedSearch = search.toLowerCase()

  // Expensive derivation — useMemo is justified when iterating large arrays
  const filtered = useMemo(
    () => products.filter(p =>
      p.name.toLowerCase().includes(normalizedSearch)
    ),
    [products, normalizedSearch]
  )

  return (
    <>
      <input value={search} onChange={e => setSearch(e.target.value)} />
      {hasSearch && <ClearButton />}
      {filtered.map(p => <ProductCard key={p.id} product={p} />)}
    </>
  )
}

When to use useMemo vs a plain const:

  • Plain const — boolean flags, string formatting, simple arithmetic, object property access, .length checks. These are essentially free and useMemo overhead is not worth it.
  • useMemo — filtering/sorting arrays, building data structures, JSON.parse, expensive transformations, anything that iterates collections or involves O(n) work.

The rule: if the expression returns a primitive or is a single property access, skip useMemo. If it iterates or transforms data, wrap it.

React Compiler note: If React Compiler is enabled, it auto-memoizes expressions and you can skip manual useMemo calls.

2. Subscribe to Coarse-Grained State, Not Raw Values

Impact: HIGH — Prevents re-renders on irrelevant changes.

If your component only cares about a derived boolean (e.g., "is mobile?"), don't subscribe to the raw value that changes continuously.

Avoid — re-renders on every pixel:

function Sidebar() {
  const width = useWindowWidth() // fires on every resize
  const isMobile = width < 768
  return <nav className={isMobile ? 'mobile' : 'desktop'}>...</nav>
}

Prefer — re-renders only when the boolean flips:

function Sidebar() {
  const isMobile = useMediaQuery('(max-width: 767px)')
  return <nav className={isMobile ? 'mobile' : 'desktop'}>...</nav>
}

This applies broadly: subscribe to isLoggedIn rather than the entire user object, hasItems rather than the full cart array, etc.


3. Extract Expensive Subtrees into Memoized Components

Impact: HIGH — Enables early returns and skip-rendering.

When a parent has fast paths (loading, error, empty), expensive children still compute if they live in the same component. Extract them so React can skip their render entirely.

Avoid — avatar computation runs even during loading:

function Profile({ user, loading }: Props) {
  const avatar = useMemo(() => processAvatar(user), [user])

  if (loading) return <Skeleton />
  return <div><img src={avatar} /></div>
}

Prefer — computation skipped when loading:

const UserAvatar = memo(function UserAvatar({ user }: { user: User }) {
  const avatar = useMemo(() => processAvatar(user), [user])
  return <img src={avatar} />
})

function Profile({ user, loading }: Props) {
  if (loading) return <Skeleton />
  return <div><UserAvatar user={user} /></div>
}
React Compiler note: The compiler auto-memoizes, making manual memo() wrapping less necessary. But extracting components for early returns is still valuable.

4. Use Lazy State Initialization

Impact: MEDIUM — Avoids wasted computation on every render.

When useState receives a function call as its initial value, that call executes on every render even though the result is only used once. Pass a function reference instead.

Avoid — buildIndex() runs every render:

const [index, setIndex] = useState(buildSearchIndex(items))

Prefer — runs only on mount:

const [index, setIndex] = useState(() => buildSearchIndex(items))

Use lazy init for: JSON.parse, localStorage reads, building data structures, heavy transformations. Skip it for simple primitives like useState(0) or useState(false).


5. Use Functional setState for Stable Callbacks

Impact: MEDIUM — Removes state variables from dependency arrays.

When a callback only needs the previous state to compute the next state, use the functional form. This eliminates the state variable from the dependency array and produces a stable callback identity.

Avoid — callback changes when count changes:

const [count, setCount] = useState(0)
const increment = useCallback(() => setCount(count + 1), [count])

Prefer — callback is always stable:

const [count, setCount] = useState(0)
const increment = useCallback(() => setCount(c => c + 1), [])

6. Put Interaction Logic in Event Handlers, Not Effects

Impact: MEDIUM — Avoids re-running side effects on dependency changes.

If a side effect is triggered by a user action (click, submit, drag), run it in the event handler. Modeling it as state + effect causes re-runs when unrelated dependencies change.

Avoid — effect re-runs when theme changes:

function Form() {
  const [submitted, setSubmitted] = useState(false)
  const theme = useContext(ThemeContext)

  useEffect(() => {
    if (submitted) {
      post('/api/register')
      showToast('Registered', theme)
    }
  }, [submitted, theme])

  return <button onClick={() => setSubmitted(true)}>Submit</button>
}

Prefer — logic in the handler:

function Form() {
  const theme = useContext(ThemeContext)

  function handleSubmit() {
    post('/api/register')
    showToast('Registered', theme)
  }

  return <button onClick={handleSubmit}>Submit</button>
}

7. Use useRef for Transient, High-Frequency Values

Impact: MEDIUM — Prevents re-renders on rapid updates.

Values that change very frequently (mouse position, scroll offset, interval ticks) but don't need to drive re-renders should live in a ref. Update the DOM directly when needed.

Avoid — re-renders on every mouse move:

function Cursor() {
  const [x, setX] = useState(0)

  useEffect(() => {
    const handler = (e: MouseEvent) => setX(e.clientX)
    window.addEventListener('mousemove', handler)
    return () => window.removeEventListener('mousemove', handler)
  }, [])

  return <div style={{ transform: `translateX(${x}px)` }} />
}

Prefer — zero re-renders:

function Cursor() {
  const ref = useRef<HTMLDivElement>(null)

  useEffect(() => {
    const handler = (e: MouseEvent) => {
      if (ref.current) {
        ref.current.style.transform = `translateX(${e.clientX}px)`
      }
    }
    window.addEventListener('mousemove', handler)
    return () => window.removeEventListener('mousemove', handler)
  }, [])

  return <div ref={ref} />
}

8. Use startTransition for Non-Urgent Updates

Impact: MEDIUM — Keeps high-priority updates (typing, clicking) responsive.

Wrap non-urgent state updates in startTransition so React can interrupt them for urgent work. This is especially useful for search filtering, tab switching, and list re-sorting.

Avoid — typing blocks while list filters:

function Search({ items }: { items: Item[] }) {
  const [query, setQuery] = useState('')
  const [filtered, setFiltered] = useState(items)

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    setQuery(e.target.value)
    setFiltered(items.filter(i => i.name.includes(e.target.value)))
  }

  return (
    <>
      <input value={query} onChange={handleChange} />
      <List items={filtered} />
    </>
  )
}

Prefer — input stays responsive:

import { useState, useTransition } from 'react'

function Search({ items }: { items: Item[] }) {
  const [query, setQuery] = useState('')
  const [filtered, setFiltered] = useState(items)
  const [isPending, startTransition] = useTransition()

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    setQuery(e.target.value)
    startTransition(() => {
      setFiltered(items.filter(i => i.name.includes(e.target.value)))
    })
  }

  return (
    <>
      <input value={query} onChange={handleChange} />
      {isPending && <Spinner />}
      <List items={filtered} />
    </>
  )
}

9. Defer State Reads to the Point of Use

Impact: MEDIUM — Avoids subscriptions to state you only read in callbacks.

Don't call hooks like useSearchParams() if you only read the value inside an event handler. Read it on demand instead.

Avoid — component re-renders on every URL change:

function ShareButton({ id }: { id: string }) {
  const [searchParams] = useSearchParams()

  const handleShare = () => {
    const ref = searchParams.get('ref')
    share(id, { ref })
  }

  return <button onClick={handleShare}>Share</button>
}

Prefer — reads on demand, no subscription:

function ShareButton({ id }: { id: string }) {
  const handleShare = () => {
    const params = new URLSearchParams(window.location.search)
    share(id, { ref: params.get('ref') })
  }

  return <button onClick={handleShare}>Share</button>
}

10. Use Stable References for Default Props

Impact: MEDIUM — Prevents memo() from being defeated by new object/array literals.

Passing [] or {} as default prop values creates new references every render, defeating memoization on child components.

Avoid — new array each render:

function Dashboard({ tabs = [] }: { tabs?: Tab[] }) {
  return <TabBar tabs={tabs} /> {/* TabBar re-renders every time */}
}

Prefer — stable reference:

const EMPTY_TABS: Tab[] = []

function Dashboard({ tabs = EMPTY_TABS }: { tabs?: Tab[] }) {
  return <TabBar tabs={tabs} />
}

11. CSS content-visibility for Long Lists

Impact: HIGH — 5-10x faster initial render for long scrollable content.

Apply content-visibility: auto to off-screen items so the browser skips their layout and paint until they scroll into view.

.list-item {
  content-visibility: auto;
  contain-intrinsic-size: 0 80px; /* estimated height */
}
function MessageList({ messages }: { messages: Message[] }) {
  return (
    <div style={{ overflowY: 'auto', height: '100vh' }}>
      {messages.map(msg => (
        <div key={msg.id} className="list-item">
          <MessageCard message={msg} />
        </div>
      ))}
    </div>
  )
}

For 1,000 items, the browser skips layout and paint for ~990 off-screen items. Combine with virtualization (e.g., react-window, @tanstack/react-virtual) for truly massive lists.


12. Hoist Static JSX Outside Components

Impact: LOW — Avoids re-creating identical elements.

JSX elements that never change can be lifted to module scope. React reuses the same object reference across renders.

Avoid — recreated every render:

function Page() {
  return (
    <main>
      <footer>
        <p>Copyright 2026 Acme Inc.</p>
      </footer>
    </main>
  )
}

Prefer — created once:

const footer = (
  <footer>
    <p>Copyright 2026 Acme Inc.</p>
  </footer>
)

function Page() {
  return <main>{footer}</main>
}

Most impactful for large SVG elements which are expensive to recreate.

React Compiler note: The compiler auto-hoists static JSX, making this manual optimization unnecessary.

13. Initialize Expensive Operations Once Per App

Impact: LOW-MEDIUM — Avoids duplicate init in Strict Mode and remounts.

App-wide initialization (analytics, auth checks, service workers) should not live in useEffect — components remount in development and in concurrent features. Use a module-level guard.

Avoid — runs twice in dev, again on remount:

function App() {
  useEffect(() => {
    initAnalytics()
    checkAuth()
  }, [])
  return <Router />
}

Prefer — once per app load:

let initialized = false

function App() {
  useEffect(() => {
    if (initialized) return
    initialized = true
    initAnalytics()
    checkAuth()
  }, [])
  return <Router />
}

Or initialize at the module level in your entry file (main.tsx), outside any component.


14. Store Event Handlers in Refs for Stable Subscriptions

Impact: LOW — Prevents effect re-subscriptions.

When a custom hook subscribes to an event and accepts a callback, store the callback in a ref so the subscription doesn't tear down and recreate on every render.

Avoid — re-subscribes when handler changes:

function useWindowEvent(event: string, handler: (e: Event) => void) {
  useEffect(() => {
    window.addEventListener(event, handler)
    return () => window.removeEventListener(event, handler)
  }, [event, handler])
}

Prefer — stable subscription:

function useWindowEvent(event: string, handler: (e: Event) => void) {
  const saved = useRef(handler)
  useEffect(() => { saved.current = handler }, [handler])

  useEffect(() => {
    const listener = (e: Event) => saved.current(e)
    window.addEventListener(event, listener)
    return () => window.removeEventListener(event, listener)
  }, [event])
}

If using React 19+, useEffectEvent provides this pattern as a built-in:

import { useEffectEvent } from 'react'

function useWindowEvent(event: string, handler: (e: Event) => void) {
  const onEvent = useEffectEvent(handler)
  useEffect(() => {
    window.addEventListener(event, onEvent)
    return () => window.removeEventListener(event, onEvent)
  }, [event])
}

15. Prevent Hydration Flicker for Client-Only Data

Impact: MEDIUM — Eliminates flash of wrong content during SSR hydration.

When rendering depends on client-only data (localStorage, cookies), an inline script can set the correct value before React hydrates — avoiding both SSR errors and a visible flash.

function ThemeRoot({ children }: { children: React.ReactNode }) {
  return (
    <>
      <div id="app-root">{children}</div>
      <script
        dangerouslySetInnerHTML={{
          __html: `(function(){
            try {
              var t = localStorage.getItem('theme') || 'light';
              document.getElementById('app-root').dataset.theme = t;
            } catch(e) {}
          })();`,
        }}
      />
    </>
  )
}

This approach works in any SSR setup — Next.js, Remix, or a custom Vite SSR pipeline.


16. Never Define Components Inside Components

Impact: HIGH — Causes remounting, state loss, and wasted DOM work every render.

When you define a component inside another component's render, React creates a new component type on every render. This means the entire subtree unmounts and remounts — losing all state, DOM nodes, and effect cleanup/setup.

Avoid — Row is a new type every render:

function Table({ data }: { data: Item[] }) {
  // This creates a NEW component type on every render
  function Row({ item }: { item: Item }) {
    const [selected, setSelected] = useState(false)
    return <tr onClick={() => setSelected(!selected)}>{item.name}</tr>
  }

  return <table>{data.map(item => <Row key={item.id} item={item} />)}</table>
}

Prefer — Row defined at module scope:

function Row({ item }: { item: Item }) {
  const [selected, setSelected] = useState(false)
  return <tr onClick={() => setSelected(!selected)}>{item.name}</tr>
}

function Table({ data }: { data: Item[] }) {
  return <table>{data.map(item => <Row key={item.id} item={item} />)}</table>
}

This also applies to components defined inside useMemo, useCallback, or any other hook. Always define components at module scope or as static properties.


17. Use useDeferredValue for Expensive Derived Renders

Impact: HIGH — Keeps the UI responsive while expensive subtrees re-render in the background.

useDeferredValue tells React to defer re-rendering components that depend on a fast-changing value. Unlike useTransition (which wraps the state update), useDeferredValue wraps the consumption — useful when you don't control the state setter.

Avoid — every keystroke blocks the UI:

function SearchPage({ query }: { query: string }) {
  // Expensive: filters and renders 10,000 items on every keystroke
  const results = filterItems(query)
  return <ResultsList items={results} />
}

Prefer — input stays responsive, results update in background:

import { useDeferredValue, useMemo } from 'react'

function SearchPage({ query }: { query: string }) {
  const deferredQuery = useDeferredValue(query)
  const isStale = query !== deferredQuery

  const results = useMemo(() => filterItems(deferredQuery), [deferredQuery])

  return (
    <div style={{ opacity: isStale ? 0.7 : 1 }}>
      <ResultsList items={results} />
    </div>
  )
}

When to use useDeferredValue vs useTransition:

  • useTransition — you control the state setter and can wrap it in startTransition
  • useDeferredValue — the value comes from props, a parent, or a library you don't control

18. Use Explicit Checks in Conditional Rendering

Impact: MEDIUM — Prevents rendering 0, NaN, or empty strings to the DOM.

The && operator in JSX short-circuits on falsy values — but 0, NaN, and "" are falsy yet still render as visible text nodes.

Avoid — renders 0 to the DOM when count is zero:

function NotificationBadge({ count }: { count: number }) {
  return <div>{count && <Badge>{count}</Badge>}</div>
  // When count is 0, renders: <div>0</div>
}

Prefer — explicit boolean check:

function NotificationBadge({ count }: { count: number }) {
  return <div>{count > 0 && <Badge>{count}</Badge>}</div>
}

// Or use a ternary for clarity
function NotificationBadge({ count }: { count: number }) {
  return <div>{count > 0 ? <Badge>{count}</Badge> : null}</div>
}

This applies to any value that might be 0, NaN, or "" — array lengths, string values, numeric props. Always use an explicit boolean expression (> 0, !== '', != null) rather than relying on truthiness.


19. Narrow Effect Dependencies to Primitives

Impact: MEDIUM — Prevents effects from re-running when unrelated object properties change.

When an effect only needs one property from an object, extract it before the dependency array. Passing the whole object causes re-runs whenever any property changes.

Avoid — effect re-runs when user.name or user.avatar changes:

function UserStatus({ user }: { user: User }) {
  useEffect(() => {
    updatePresence(user.id)
  }, [user]) // re-runs on ANY user property change
}

Prefer — only re-runs when the ID changes:

function UserStatus({ user }: { user: User }) {
  const { id } = user
  useEffect(() => {
    updatePresence(id)
  }, [id])
}

This also applies to hook return values. If useQuery returns {data, status, fetchStatus} and your effect only cares about status, destructure first.


20. Split Combined Hook Computations

Impact: MEDIUM — Prevents re-renders for consumers that only need part of a hook's output.

When a custom hook computes multiple unrelated values, a change in one forces re-renders in all consumers — even those that only read the unchanged value.

Avoid — changing total re-renders components that only need average:

function useStats(items: number[]) {
  return useMemo(() => ({
    total: items.reduce((a, b) => a + b, 0),
    average: items.reduce((a, b) => a + b, 0) / items.length,
    max: Math.max(...items),
  }), [items])
}

Prefer — split into focused hooks:

function useTotal(items: number[]) {
  return useMemo(() => items.reduce((a, b) => a + b, 0), [items])
}

function useAverage(items: number[]) {
  return useMemo(() => items.reduce((a, b) => a + b, 0) / items.length, [items])
}

function useMax(items: number[]) {
  return useMemo(() => Math.max(...items), [items])
}

Components call only the hook they need. If a single component needs all three, combining them there is fine — the split prevents unnecessary coupling at the hook level.


21. Avoid Layout Thrashing with Batched DOM Reads/Writes

Impact: HIGH — Prevents forced synchronous layouts that block the main thread.

Reading a layout property (e.g., offsetHeight, getBoundingClientRect()) after writing to the DOM forces the browser to recalculate layout synchronously. In a loop, this creates layout thrashing.

Avoid — forces layout recalculation on every iteration:

function resizeCards(cards: HTMLElement[]) {
  cards.forEach(card => {
    const height = card.offsetHeight          // READ (forces layout)
    card.style.minHeight = `${height + 20}px` // WRITE (invalidates layout)
  })
}

Prefer — batch all reads, then all writes:

function resizeCards(cards: HTMLElement[]) {
  // Read phase
  const heights = cards.map(card => card.offsetHeight)

  // Write phase
  cards.forEach((card, i) => {
    card.style.minHeight = `${heights[i] + 20}px`
  })
}

In React, this most commonly occurs in useLayoutEffect or useEffect callbacks that measure and mutate DOM elements. When you need to read layout inside an animation frame, use requestAnimationFrame to batch:

useLayoutEffect(() => {
  const measurements = items.map(el => el.getBoundingClientRect())

  requestAnimationFrame(() => {
    items.forEach((el, i) => {
      el.style.transform = `translateY(${measurements[i].top}px)`
    })
  })
}, [items])

22. Animate SVG Wrappers, Not SVG Elements Directly

Impact: MEDIUM — Avoids repainting the entire SVG on every animation frame.

Animating properties on an SVG element itself (e.g., <svg> or <path>) triggers a full SVG repaint. Wrap the SVG in a <div> and animate the wrapper instead.

Avoid — repaints entire SVG tree:

<motion.svg animate={{ rotate: 360 }} style={{ width: 200, height: 200 }}>
  <ComplexChart />
</motion.svg>

Prefer — only the wrapper repaints:

<motion.div animate={{ rotate: 360 }} style={{ width: 200, height: 200 }}>
  <svg viewBox="0 0 200 200">
    <ComplexChart />
  </svg>
</motion.div>

This also applies to CSS animations. Use transform on a wrapper element rather than animating SVG attributes like cx, cy, or d directly.


23. Suppress Expected Hydration Mismatches

Impact: LOW-MEDIUM — Silences known-safe warnings without hiding real bugs.

Some content is intentionally different between server and client — timestamps, random IDs, user-agent-specific rendering. Use suppressHydrationWarning on those specific elements.

function Comment({ createdAt }: { createdAt: Date }) {
  return (
    <article>
      <p>{comment.body}</p>
      <time suppressHydrationWarning>
        {formatRelativeTime(createdAt)} {/* "2 minutes ago" differs server vs client */}
      </time>
    </article>
  )
}

Apply sparingly and only on leaf elements. Never suppress warnings on container elements — it masks real mismatches in children.


24. React DOM Resource Hints for Vite SPAs

Impact: HIGH — Lets the browser start loading critical resources earlier without framework support.

React 19 adds preload() and preinit() from react-dom — imperative resource hints that work in any React app. In Vite SPAs (which don't get framework-level prefetching), these are especially valuable.

import { preload, preinit } from 'react-dom'

function App() {
  // Preload a font before it's needed
  preload('/fonts/inter-var.woff2', { as: 'font', type: 'font/woff2', crossOrigin: 'anonymous' })

  // Preinit a critical CSS file (loads + applies it)
  preinit('/critical.css', { as: 'style' })

  return <RouterProvider router={router} />
}

On navigation — preload the next page's data and code:

function ProductLink({ id }: { id: string }) {
  const handleHover = () => {
    // Preload the image the next page will need
    preload(`/api/products/${id}/image.webp`, { as: 'image' })
    // Prefetch the route code
    import('./pages/ProductDetail')
  }

  return <Link to={`/products/${id}`} onMouseEnter={handleHover}>View</Link>
}

These are no-ops if the resource is already loaded, so calling them eagerly is safe. For Vite apps without a meta-framework, this is the primary mechanism for resource prioritization.


25. Use useTransition for Route Navigation

Impact: MEDIUM — Keeps the current page interactive while the next route loads.

In Vite SPAs with React.lazy() routes, clicking a navigation link can freeze the UI while the chunk loads and the component renders. Wrapping navigation in startTransition lets React show the old page until the new one is ready.

import { useTransition } from 'react'
import { useNavigate } from 'react-router-dom'

function NavLink({ to, children }: { to: string; children: React.ReactNode }) {
  const navigate = useNavigate()
  const [isPending, startTransition] = useTransition()

  const handleClick = (e: React.MouseEvent) => {
    e.preventDefault()
    startTransition(() => {
      navigate(to)
    })
  }

  return (
    <a
      href={to}
      onClick={handleClick}
      style={{ opacity: isPending ? 0.7 : 1 }}
    >
      {children}
    </a>
  )
}

This prevents the blank-screen flash between lazy-loaded routes and gives you isPending to show a subtle loading indicator on the current page.


Source

Patterns from patterns.dev — framework-agnostic React performance guidance for the broader web engineering community.

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

34.79%
按下载量换算920

Claude

29.88%
按下载量换算790

Cursor

17.81%
按下载量换算471

Gemini CLI

8.91%
按下载量换算236

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills