安装
npx skills add https://github.com/affaan-m/everything-claude-code --skill frontend-patterns
- Frontend Development Patterns
- Modern frontend patterns for React, Next.js, and performant user interfaces.
- When to Activate
- Building React components (composition, props, rendering)
- Managing state (useState, useReducer, Zustand, Context)
- Implementing data fetching (SWR, React Query, server components)
- Optimizing performance (memoization, virtualization, code splitting)
- Working with forms (validation, controlled inputs, Zod schemas)
- Handling client-side routing and navigation
- Building accessible, responsive UI patterns
- Component Patterns
- Composition Over Inheritance
- // ✅ 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
- >
- Compound Components
- 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
- >
- Render Props Pattern
- 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
- >
- Custom Hooks Patterns
- State Management 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
- (
- )
- Async Data Fetching 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
- )
- const
- refetch
- =
- useCallback
- (
- async
- (
- )
- =>
- {
- setLoading
- (
- true
- )
- setError
- (
- null
- )
- try
- {
- const
- result
- =
- await
- fetcher
- (
- )
- setData
- (
- result
- )
- options
- ?.
- onSuccess
- ?.
- (
- result
- )
- }
- catch
- (
- err
- )
- {
- const
- error
- =
- err
- as
- Error
- setError
- (
- error
- )
- options
- ?.
- onError
- ?.
- (
- error
- )
- }
- finally
- {
- setLoading
- (
- false
- )
- }
- }
- ,
- [
- fetcher
- ,
- options
- ]
- )
- useEffect
- (
- (
- )
- =>
- {
- if
- (
- options
- ?.
- enabled
- !==
- false
- )
- {
- refetch
- (
- )
- }
- }
- ,
- [
- key
- ,
- refetch
- ,
- options
- ?.
- enabled
- ]
- )
- 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
- )
- }
- )
- Debounce 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
- ]
- )
- State Management Patterns
- Context + Reducer Pattern
- 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
- }
- Performance Optimization
- Memoization
- // ✅ useMemo for expensive computations
- const
- sortedMarkets
- =
- useMemo
- (
- (
- )
- =>
- {
- return
- markets
- .
- sort
- (
- (
- a
- ,
- b
- )
- =>
- b
- .
- volume
- -
- a
- .
- volume
- )
- }
- ,
- [
- markets
- ]
- )
- // ✅ useCallback for functions passed to children
- const
- handleSearch
- =
- useCallback
- (
- (
- query
- :
- string
- )
- =>
- {
- setSearchQuery
- (
- query
- )
- }
- ,
- [
- ]
- )
- // ✅ 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
- >
- )
- }
- )
- Code Splitting & Lazy Loading
- import
- {
- lazy
- ,
- Suspense
- }
- from
- 'react'
- // ✅ 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
- >
- )
- }
- Virtualization for Long Lists
- 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
- >
- )
- }
- Form Handling Patterns
- Controlled Form with Validation
- 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
- >
- )
- }
- Error Boundary Pattern
- 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
- >
- Animation Patterns
- Framer Motion Animations
- import
- {
- motion
- ,
- AnimatePresence
- }
- from
- 'framer-motion'
- // ✅ 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
- >
- )
- }
- // ✅ 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
- >
- )
- }
- Accessibility Patterns
- Keyboard Navigation
- 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
- >
- )
- }
- Focus Management
- 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
- }
- Remember
- Modern frontend patterns enable maintainable, performant user interfaces. Choose patterns that fit your project complexity.
← 返回排行榜