Source profileQuality 64/100

affaan-m/ECC/docs/zh-CN/skills/frontend-patterns/SKILL.md

frontend-patterns

Use it for engineering and design tasks; the detail page covers purpose, installation, and practical steps.

Source repository stars
234,327
Declared platforms
0
Static risk flags
1
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

适用于 React、Next.js 和高性能用户界面的现代前端模式。

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/frontend-patterns"
    Safe inspection promptEditorial

    Inspect the Agent Skill "frontend-patterns" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/zh-CN/skills/frontend-patterns/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      何时激活

      构建 React 组件(组合、属性、渲染) 管理状态(useState、useReducer、Zustand、Context) 实现数据获取(SWR、React Query、服务器组件) 优化性能(记忆化、虚拟化、代码分割) 处理表单(验证、受控输入、Zod 模式) 处理客户端路由和导航 构建可访问、响应式的 UI 模式

      构建 React 组件(组合、属性、渲染)管理状态(useState、useReducer、Zustand、Context)实现数据获取(SWR、React Query、服务器组件)
    2. 02

      组件模式

      Review the “组件模式” section in the pinned source before continuing.

      Review and apply the “组件模式” source section.
    3. 03

      组合优于继承

      Review the “组合优于继承” section in the pinned source before continuing.

      Review and apply the “组合优于继承” source section.
    4. 04

      复合组件

      Review the “复合组件” section in the pinned source before continuing.

      Review and apply the “复合组件” source section.
    5. 05

      渲染属性模式

      Review the “渲染属性模式” section in the pinned source before continuing.

      Review and apply the “渲染属性模式” source section.

    Permission review

    Static risk signals and limitations

    Network access

    medium · line 109

    The documentation includes network, browsing, or remote request actions.

    fetch(url)

    Network access

    medium · line 208

    The documentation includes network, browsing, or remote request actions.

    () => fetch('/api/markets').then(r => r.json()),

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score64/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    docs/zh-CN/skills/frontend-patterns/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    前端开发模式

    适用于 React、Next.js 和高性能用户界面的现代前端模式。

    何时激活

    • 构建 React 组件(组合、属性、渲染)
    • 管理状态(useState、useReducer、Zustand、Context)
    • 实现数据获取(SWR、React Query、服务器组件)
    • 优化性能(记忆化、虚拟化、代码分割)
    • 处理表单(验证、受控输入、Zod 模式)
    • 处理客户端路由和导航
    • 构建可访问、响应式的 UI 模式

    组件模式

    组合优于继承

    // PASS: GOOD: Component composition
    interface CardProps {
      children: React.ReactNode
      variant?: 'default' | 'outlined'
    }
    
    export function Card({ children, variant = 'default' }: CardProps) {
      return <div className={`card card-${variant}`}>{children}</div>
    }
    
    export function CardHeader({ children }: { children: React.ReactNode }) {
      return <div className="card-header">{children}</div>
    }
    
    export function CardBody({ children }: { children: React.ReactNode }) {
      return <div className="card-body">{children}</div>
    }
    
    // Usage
    <Card>
      <CardHeader>Title</CardHeader>
      <CardBody>Content</CardBody>
    </Card>
    

    复合组件

    interface TabsContextValue {
      activeTab: string
      setActiveTab: (tab: string) => void
    }
    
    const TabsContext = createContext<TabsContextValue | undefined>(undefined)
    
    export function Tabs({ children, defaultTab }: {
      children: React.ReactNode
      defaultTab: string
    }) {
      const [activeTab, setActiveTab] = useState(defaultTab)
    
      return (
        <TabsContext.Provider value={{ activeTab, setActiveTab }}>
          {children}
        </TabsContext.Provider>
      )
    }
    
    export function TabList({ children }: { children: React.ReactNode }) {
      return <div className="tab-list">{children}</div>
    }
    
    export function Tab({ id, children }: { id: string, children: React.ReactNode }) {
      const context = useContext(TabsContext)
      if (!context) throw new Error('Tab must be used within Tabs')
    
      return (
        <button
          className={context.activeTab === id ? 'active' : ''}
          onClick={() => context.setActiveTab(id)}
        >
          {children}
        </button>
      )
    }
    
    // Usage
    <Tabs defaultTab="overview">
      <TabList>
        <Tab id="overview">Overview</Tab>
        <Tab id="details">Details</Tab>
      </TabList>
    </Tabs>
    

    渲染属性模式

    interface DataLoaderProps<T> {
      url: string
      children: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode
    }
    
    export function DataLoader<T>({ url, children }: DataLoaderProps<T>) {
      const [data, setData] = useState<T | null>(null)
      const [loading, setLoading] = useState(true)
      const [error, setError] = useState<Error | null>(null)
    
      useEffect(() => {
        fetch(url)
          .then(res => res.json())
          .then(setData)
          .catch(setError)
          .finally(() => setLoading(false))
      }, [url])
    
      return <>{children(data, loading, error)}</>
    }
    
    // Usage
    <DataLoader<Market[]> url="/api/markets">
      {(markets, loading, error) => {
        if (loading) return <Spinner />
        if (error) return <Error error={error} />
        return <MarketList markets={markets!} />
      }}
    </DataLoader>
    

    自定义 Hooks 模式

    状态管理 Hook

    export function useToggle(initialValue = false): [boolean, () => void] {
      const [value, setValue] = useState(initialValue)
    
      const toggle = useCallback(() => {
        setValue(v => !v)
      }, [])
    
      return [value, toggle]
    }
    
    // Usage
    const [isOpen, toggleOpen] = useToggle()
    

    异步数据获取 Hook

    interface UseQueryOptions<T> {
      onSuccess?: (data: T) => void
      onError?: (error: Error) => void
      enabled?: boolean
    }
    
    export function useQuery<T>(
      key: string,
      fetcher: () => Promise<T>,
      options?: UseQueryOptions<T>
    ) {
      const [data, setData] = useState<T | null>(null)
      const [error, setError] = useState<Error | null>(null)
      const [loading, setLoading] = useState(false)
    
      // Keep the latest fetcher/options in refs so refetch stays referentially
      // stable even when callers pass inline functions and object literals.
      // Without this, every render creates a new refetch, and the effect below
      // re-runs after each state update - an infinite fetch loop.
      const fetcherRef = useRef(fetcher)
      const optionsRef = useRef(options)
      useEffect(() => {
        fetcherRef.current = fetcher
        optionsRef.current = options
      })
    
      const refetch = useCallback(async () => {
        setLoading(true)
        setError(null)
    
        try {
          const result = await fetcherRef.current()
          setData(result)
          optionsRef.current?.onSuccess?.(result)
        } catch (err) {
          const error = err as Error
          setError(error)
          optionsRef.current?.onError?.(error)
        } finally {
          setLoading(false)
        }
      }, [])
    
      const enabled = options?.enabled !== false
    
      useEffect(() => {
        if (enabled) {
          refetch()
        }
      }, [key, enabled, refetch])
    
      return { data, error, loading, refetch }
    }
    
    // Usage
    const { data: markets, loading, error, refetch } = useQuery(
      'markets',
      () => fetch('/api/markets').then(r => r.json()),
      {
        onSuccess: data => console.log('Fetched', data.length, 'markets'),
        onError: err => console.error('Failed:', err)
      }
    )
    

    防抖 Hook

    export function useDebounce<T>(value: T, delay: number): T {
      const [debouncedValue, setDebouncedValue] = useState<T>(value)
    
      useEffect(() => {
        const handler = setTimeout(() => {
          setDebouncedValue(value)
        }, delay)
    
        return () => clearTimeout(handler)
      }, [value, delay])
    
      return debouncedValue
    }
    
    // Usage
    const [searchQuery, setSearchQuery] = useState('')
    const debouncedQuery = useDebounce(searchQuery, 500)
    
    useEffect(() => {
      if (debouncedQuery) {
        performSearch(debouncedQuery)
      }
    }, [debouncedQuery])
    

    状态管理模式

    Context + Reducer 模式

    interface State {
      markets: Market[]
      selectedMarket: Market | null
      loading: boolean
    }
    
    type Action =
      | { type: 'SET_MARKETS'; payload: Market[] }
      | { type: 'SELECT_MARKET'; payload: Market }
      | { type: 'SET_LOADING'; payload: boolean }
    
    function reducer(state: State, action: Action): State {
      switch (action.type) {
        case 'SET_MARKETS':
          return { ...state, markets: action.payload }
        case 'SELECT_MARKET':
          return { ...state, selectedMarket: action.payload }
        case 'SET_LOADING':
          return { ...state, loading: action.payload }
        default:
          return state
      }
    }
    
    const MarketContext = createContext<{
      state: State
      dispatch: Dispatch<Action>
    } | undefined>(undefined)
    
    export function MarketProvider({ children }: { children: React.ReactNode }) {
      const [state, dispatch] = useReducer(reducer, {
        markets: [],
        selectedMarket: null,
        loading: false
      })
    
      return (
        <MarketContext.Provider value={{ state, dispatch }}>
          {children}
        </MarketContext.Provider>
      )
    }
    
    export function useMarkets() {
      const context = useContext(MarketContext)
      if (!context) throw new Error('useMarkets must be used within MarketProvider')
      return context
    }
    

    性能优化

    记忆化

    // PASS: useMemo for expensive computations
    // Copy before sorting - Array.prototype.sort mutates in place
    const sortedMarkets = useMemo(() => {
      return [...markets].sort((a, b) => b.volume - a.volume)
    }, [markets])
    
    // PASS: useCallback for functions passed to children
    const handleSearch = useCallback((query: string) => {
      setSearchQuery(query)
    }, [])
    
    // PASS: React.memo for pure components
    export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
      return (
        <div className="market-card">
          <h3>{market.name}</h3>
          <p>{market.description}</p>
        </div>
      )
    })
    

    代码分割与懒加载

    import { lazy, Suspense } from 'react'
    
    // PASS: Lazy load heavy components
    const HeavyChart = lazy(() => import('./HeavyChart'))
    const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
    
    export function Dashboard() {
      return (
        <div>
          <Suspense fallback={<ChartSkeleton />}>
            <HeavyChart data={data} />
          </Suspense>
    
          <Suspense fallback={null}>
            <ThreeJsBackground />
          </Suspense>
        </div>
      )
    }
    

    长列表虚拟化

    import { useVirtualizer } from '@tanstack/react-virtual'
    
    export function VirtualMarketList({ markets }: { markets: Market[] }) {
      const parentRef = useRef<HTMLDivElement>(null)
    
      const virtualizer = useVirtualizer({
        count: markets.length,
        getScrollElement: () => parentRef.current,
        estimateSize: () => 100,  // Estimated row height
        overscan: 5  // Extra items to render
      })
    
      return (
        <div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
          <div
            style={{
              height: `${virtualizer.getTotalSize()}px`,
              position: 'relative'
            }}
          >
            {virtualizer.getVirtualItems().map(virtualRow => (
              <div
                key={virtualRow.index}
                style={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  width: '100%',
                  height: `${virtualRow.size}px`,
                  transform: `translateY(${virtualRow.start}px)`
                }}
              >
                <MarketCard market={markets[virtualRow.index]} />
              </div>
            ))}
          </div>
        </div>
      )
    }
    

    表单处理模式

    带验证的受控表单

    interface FormData {
      name: string
      description: string
      endDate: string
    }
    
    interface FormErrors {
      name?: string
      description?: string
      endDate?: string
    }
    
    export function CreateMarketForm() {
      const [formData, setFormData] = useState<FormData>({
        name: '',
        description: '',
        endDate: ''
      })
    
      const [errors, setErrors] = useState<FormErrors>({})
    
      const validate = (): boolean => {
        const newErrors: FormErrors = {}
    
        if (!formData.name.trim()) {
          newErrors.name = 'Name is required'
        } else if (formData.name.length > 200) {
          newErrors.name = 'Name must be under 200 characters'
        }
    
        if (!formData.description.trim()) {
          newErrors.description = 'Description is required'
        }
    
        if (!formData.endDate) {
          newErrors.endDate = 'End date is required'
        }
    
        setErrors(newErrors)
        return Object.keys(newErrors).length === 0
      }
    
      const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault()
    
        if (!validate()) return
    
        try {
          await createMarket(formData)
          // Success handling
        } catch (error) {
          // Error handling
        }
      }
    
      return (
        <form onSubmit={handleSubmit}>
          <input
            value={formData.name}
            onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}
            placeholder="Market name"
          />
          {errors.name && <span className="error">{errors.name}</span>}
    
          {/* Other fields */}
    
          <button type="submit">Create Market</button>
        </form>
      )
    }
    

    错误边界模式

    interface ErrorBoundaryState {
      hasError: boolean
      error: Error | null
    }
    
    export class ErrorBoundary extends React.Component<
      { children: React.ReactNode },
      ErrorBoundaryState
    > {
      state: ErrorBoundaryState = {
        hasError: false,
        error: null
      }
    
      static getDerivedStateFromError(error: Error): ErrorBoundaryState {
        return { hasError: true, error }
      }
    
      componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
        console.error('Error boundary caught:', error, errorInfo)
      }
    
      render() {
        if (this.state.hasError) {
          return (
            <div className="error-fallback">
              <h2>Something went wrong</h2>
              <p>{this.state.error?.message}</p>
              <button onClick={() => this.setState({ hasError: false })}>
                Try again
              </button>
            </div>
          )
        }
    
        return this.props.children
      }
    }
    
    // Usage
    <ErrorBoundary>
      <App />
    </ErrorBoundary>
    

    动画模式

    Framer Motion 动画

    import { motion, AnimatePresence } from 'framer-motion'
    
    // PASS: List animations
    export function AnimatedMarketList({ markets }: { markets: Market[] }) {
      return (
        <AnimatePresence>
          {markets.map(market => (
            <motion.div
              key={market.id}
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -20 }}
              transition={{ duration: 0.3 }}
            >
              <MarketCard market={market} />
            </motion.div>
          ))}
        </AnimatePresence>
      )
    }
    
    // PASS: Modal animations
    export function Modal({ isOpen, onClose, children }: ModalProps) {
      return (
        <AnimatePresence>
          {isOpen && (
            <>
              <motion.div
                className="modal-overlay"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                onClick={onClose}
              />
              <motion.div
                className="modal-content"
                initial={{ opacity: 0, scale: 0.9, y: 20 }}
                animate={{ opacity: 1, scale: 1, y: 0 }}
                exit={{ opacity: 0, scale: 0.9, y: 20 }}
              >
                {children}
              </motion.div>
            </>
          )}
        </AnimatePresence>
      )
    }
    

    无障碍模式

    键盘导航

    export function Dropdown({ options, onSelect }: DropdownProps) {
      const [isOpen, setIsOpen] = useState(false)
      const [activeIndex, setActiveIndex] = useState(0)
    
      const handleKeyDown = (e: React.KeyboardEvent) => {
        switch (e.key) {
          case 'ArrowDown':
            e.preventDefault()
            setActiveIndex(i => Math.min(i + 1, options.length - 1))
            break
          case 'ArrowUp':
            e.preventDefault()
            setActiveIndex(i => Math.max(i - 1, 0))
            break
          case 'Enter':
            e.preventDefault()
            onSelect(options[activeIndex])
            setIsOpen(false)
            break
          case 'Escape':
            setIsOpen(false)
            break
        }
      }
    
      return (
        <div
          role="combobox"
          aria-expanded={isOpen}
          aria-haspopup="listbox"
          onKeyDown={handleKeyDown}
        >
          {/* Dropdown implementation */}
        </div>
      )
    }
    

    焦点管理

    export function Modal({ isOpen, onClose, children }: ModalProps) {
      const modalRef = useRef<HTMLDivElement>(null)
      const previousFocusRef = useRef<HTMLElement | null>(null)
    
      useEffect(() => {
        if (isOpen) {
          // Save currently focused element
          previousFocusRef.current = document.activeElement as HTMLElement
    
          // Focus modal
          modalRef.current?.focus()
        } else {
          // Restore focus when closing
          previousFocusRef.current?.focus()
        }
      }, [isOpen])
    
      return isOpen ? (
        <div
          ref={modalRef}
          role="dialog"
          aria-modal="true"
          tabIndex={-1}
          onKeyDown={e => e.key === 'Escape' && onClose()}
        >
          {children}
        </div>
      ) : null
    }
    

    记住:现代前端模式能实现可维护、高性能的用户界面。选择适合你项目复杂度的模式。

    Alternatives

    Compare before choosing