Skip to main content
frontend-patterns Frontend development patterns for React, Next.js, state management, performance optimization, and UI best practices.
Ir para a instalação Skills Marketplace Descubra e explore skills de IA criadas pela comunidade.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Copiar promptMostrar detalhes do prompt Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
npx skills add https://github.com/linnefromice/war-sim-game --skill frontend-patternsO comando permanece em uma só linha. Role horizontalmente para revisá-lo antes de copiar.
Prefere uma cópia local? Baixe os arquivos disponíveis atualmente no SkillsMP.
Baixar Zip Baixando... Ocupações relacionadas SOC
Baseado na classificação ocupacional SOC
name frontend-patterns description Frontend development patterns for React, Next.js, state management, performance optimization, and UI best practices.
フロントエンド開発パターン
React、Next.js、パフォーマンスの高いユーザーインターフェースのためのモダンなフロントエンドパターン。
コンポーネントパターン
継承よりコンポジション
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 = > {children}
}
( ) {
}
< >
</ >
"card-header"
</div >
export
function
CardBody
{ children }: { children: React.ReactNode }
return
<div className ="card-body" > {children}</div >
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 >
)
}
<Tabs defaultTab="overview" >
<TabList >
<Tab id ="overview" > Overview</Tab >
<Tab id ="details" > Details</Tab >
</TabList >
</Tabs >
Render Propsパターン 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)}</>
}
<DataLoader <Market []> url="/api/markets" >
{(markets, loading, error ) => {
if (loading) return <Spinner />
if (error) return <Error error ={error} />
return <MarketList markets ={markets!} />
}}
</DataLoader >
カスタムフックパターン
状態管理フック export function useToggle (initialValue = false ): [boolean , () => void ] {
const [value, setValue] = useState (initialValue)
const toggle = useCallback (() => {
setValue (v => !v)
}, [])
return [value, toggle]
}
const [isOpen, toggleOpen] = useToggle ()
非同期データフェッチフック 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 }
}
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フック 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
}
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
}
パフォーマンス最適化
メモ化
const sortedMarkets = useMemo (() => {
return markets.sort ((a, b ) => b.volume - a.volume )
}, [markets])
const handleSearch = useCallback ((query : string ) => {
setSearchQuery (query)
}, [])
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'
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 ,
overscan : 5
})
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 = '名前は必須です'
} else if (formData.name .length > 200 ) {
newErrors.name = '名前は200文字以内にしてください'
}
if (!formData.description .trim ()) {
newErrors.description = '説明は必須です'
}
if (!formData.endDate ) {
newErrors.endDate = '終了日は必須です'
}
setErrors (newErrors)
return Object .keys (newErrors).length === 0
}
const handleSubmit = async (e : React .FormEvent ) => {
e.preventDefault ()
if (!validate ()) return
try {
await createMarket (formData)
} catch (error) {
}
}
return (
<form onSubmit ={handleSubmit} >
<input
value ={formData.name}
onChange ={e => setFormData(prev => ({ ...prev, name: e.target.value }))}
placeholder="マーケット名"
/>
{errors.name && <span className ="error" > {errors.name}</span > }
{/* その他のフィールド */}
<button type ="submit" > マーケットを作成</button >
</form >
)
}
Error Boundaryパターン 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 > 問題が発生しました</h2 >
<p > {this.state.error?.message}</p >
<button onClick ={() => this.setState({ hasError: false })}>
もう一度試す
</button >
</div >
)
}
return this .props .children
}
}
<ErrorBoundary >
<App />
</ErrorBoundary >
アニメーションパターン
Framer Motionアニメーション import { motion, AnimatePresence } from 'framer-motion'
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 >
)
}
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}
>
{/* ドロップダウンの実装 */}
</div >
)
}
フォーカス管理 export function Modal ({ isOpen, onClose, children }: ModalProps ) {
const modalRef = useRef<HTMLDivElement >(null )
const previousFocusRef = useRef<HTMLElement | null >(null )
useEffect (() => {
if (isOpen) {
previousFocusRef.current = document .activeElement as HTMLElement
modalRef.current ?.focus ()
} else {
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
}
覚えておくこと : モダンなフロントエンドパターンは保守性が高くパフォーマンスの良いユーザーインターフェースを実現する。プロジェクトの複雑さに合ったパターンを選択すること。