Frontend Patterns

affaan-m/ECC/docs/tr/skills/frontend-patterns

by affaan-mef648e01899ba3e8dc6371642deaaf64b4477775No license275K starsListed Oct 9, 2026Updated Oct 9, 2026Repository updated 4 days ago

React, Next.js, state yönetimi, performans optimizasyonu ve UI en iyi uygulamaları için frontend geliştirme kalıpları.

Instructions onlySoftware Development
AI-generated overview

Reference patterns for building React and Next.js frontends: components, hooks, state, performance, forms and accessibility.

What it does
Provides a catalogue of modern frontend development patterns for React and Next.js, covering component composition, compound components, render props, custom hooks, state management with Context and reducers, memoization, code splitting, virtualization, controlled forms, error boundaries, animation and accessibility. Each pattern is shown as a TypeScript code example with usage notes. It is an instructional reference document and produces no files or artifacts.
When to use it
Use it when writing or reviewing React components, managing state, implementing data fetching, optimizing rendering performance, handling forms, or building accessible and responsive UI. It is meant as guidance during frontend implementation work.
Requirements
No scripts or tooling are included; it is instructions only. The examples reference React, Next.js, TypeScript, Zustand, SWR, React Query, Zod, @tanstack/react-virtual and framer-motion, but nothing is installed or executed by the skill.

Frontend Geliştirme Kalıpları

React, Next.js ve performanslı kullanıcı arayüzleri için modern frontend kalıpları.

Ne Zaman Aktifleştirmelisiniz

  • React bileşenleri oluştururken (composition, props, rendering)
  • State yönetirken (useState, useReducer, Zustand, Context)
  • Veri çekme implementasyonu (SWR, React Query, server components)
  • Performans optimize ederken (memoization, virtualization, code splitting)
  • Formlarla çalışırken (validation, controlled inputs, Zod schemas)
  • Client-side routing ve navigasyon işlerken
  • Erişilebilir, responsive UI kalıpları oluştururken

Bileşen Kalıpları

Kalıtım Yerine Composition

typescript
// PASS: İYİ: Bileşen compositioninterface 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>}
// Kullanım<Card>  <CardHeader>Başlık</CardHeader>  <CardBody>İçerik</CardBody></Card>

Compound Components

typescript
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>  )}
// Kullanım<Tabs defaultTab="overview">  <TabList>    <Tab id="overview">Genel Bakış</Tab>    <Tab id="details">Detaylar</Tab>  </TabList></Tabs>

Render Props Kalıbı

typescript
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)}</>}
// Kullanım<DataLoader<Market[]> url="/api/markets">  {(markets, loading, error) => {    if (loading) return <Spinner />    if (error) return <Error error={error} />    return <MarketList markets={markets!} />  }}</DataLoader>

Özel Hook Kalıpları

State Yönetimi Hook'u

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

Async Veri Çekme Hook'u

typescript
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)
  // Çağıranlar satır içi fonksiyonlar ve nesne literalleri geçirse bile  // refetch'in referans olarak kararlı kalması için en güncel fetcher/options  // değerlerini ref'lerde tutun. Bu olmadan her render yeni bir refetch  // oluşturur ve aşağıdaki effect her state güncellemesinden sonra yeniden  // çalışır - sonsuz bir fetch döngüsü.  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 }}
// Kullanımconst { data: markets, loading, error, refetch } = useQuery(  'markets',  () => fetch('/api/markets').then(r => r.json()),  {    onSuccess: data => console.log('Getirilen', data.length, 'market'),    onError: err => console.error('Başarısız:', err)  })

Debounce Hook'u

typescript
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}
// Kullanımconst [searchQuery, setSearchQuery] = useState('')const debouncedQuery = useDebounce(searchQuery, 500)
useEffect(() => {  if (debouncedQuery) {    performSearch(debouncedQuery)  }}, [debouncedQuery])

State Yönetimi Kalıpları

Context + Reducer Kalıbı

typescript
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}

Performans Optimizasyonu

Memoization

typescript
// PASS: Pahalı hesaplamalar için useMemo// Sıralamadan önce kopyalayın - Array.prototype.sort yerinde değiştirirconst sortedMarkets = useMemo(() => {  return [...markets].sort((a, b) => b.volume - a.volume)}, [markets])
// PASS: Alt bileşenlere geçirilen fonksiyonlar için useCallbackconst handleSearch = useCallback((query: string) => {  setSearchQuery(query)}, [])
// PASS: Pure bileşenler için React.memoexport const MarketCard = React.memo<MarketCardProps>(({ market }) => {  return (    <div className="market-card">      <h3>{market.name}</h3>      <p>{market.description}</p>    </div>  )})

Code Splitting ve Lazy Loading

typescript
import { lazy, Suspense } from 'react'
// PASS: Ağır bileşenleri lazy yükleconst 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>  )}

Uzun Listeler için Virtualization

typescript
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,  // Tahmini satır yüksekliği    overscan: 5  // Ekstra render edilecek öğeler  })
  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 İşleme Kalıpları

Doğrulamalı Controlled Form

typescript
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 = 'İsim gereklidir'    } else if (formData.name.length > 200) {      newErrors.name = 'İsim 200 karakterden az olmalıdır'    }
    if (!formData.description.trim()) {      newErrors.description = 'Açıklama gereklidir'    }
    if (!formData.endDate) {      newErrors.endDate = 'Bitiş tarihi gereklidir'    }
    setErrors(newErrors)    return Object.keys(newErrors).length === 0  }
  const handleSubmit = async (e: React.FormEvent) => {    e.preventDefault()
    if (!validate()) return
    try {      await createMarket(formData)      // Başarı işleme    } catch (error) {      // Hata işleme    }  }
  return (    <form onSubmit={handleSubmit}>      <input        value={formData.name}        onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}        placeholder="Market ismi"      />      {errors.name && <span className="error">{errors.name}</span>}
      {/* Diğer alanlar */}
      <button type="submit">Market Oluştur</button>    </form>  )}

Error Boundary Kalıbı

typescript
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>Bir şeyler yanlış gitti</h2>          <p>{this.state.error?.message}</p>          <button onClick={() => this.setState({ hasError: false })}>            Tekrar dene          </button>        </div>      )    }
    return this.props.children  }}
// Kullanım<ErrorBoundary>  <App /></ErrorBoundary>

Animasyon Kalıpları

Framer Motion Animasyonları

typescript
import { motion, AnimatePresence } from 'framer-motion'
// PASS: Liste animasyonları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 animasyonları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>  )}

Erişilebilirlik Kalıpları

Klavye Navigasyonu

typescript
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 implementasyonu */}    </div>  )}

Focus Yönetimi

typescript
export function Modal({ isOpen, onClose, children }: ModalProps) {  const modalRef = useRef<HTMLDivElement>(null)  const previousFocusRef = useRef<HTMLElement | null>(null)
  useEffect(() => {    if (isOpen) {      // Şu anki focus'lanmış elementi kaydet      previousFocusRef.current = document.activeElement as HTMLElement
      // Modal'a focus yap      modalRef.current?.focus()    } else {      // Kapatırken focus'u geri yükle      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}

Unutmayın: Modern frontend kalıpları sürdürülebilir, performanslı kullanıcı arayüzleri sağlar. Proje karmaşıklığınıza uyan kalıpları seçin.

Source and attribution

Source:affaan-m/ECCindocs/tr/skills/frontend-patternsat commitef648e0

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal