React / Patterns

    Wzorce projektowe React

    Compound Components, Custom Hooks, Provider Pattern i Error Boundaries — najważniejsze wzorce dla skalowalnych aplikacji React.

    Compound
    Wspólny stan
    Custom Hooks
    Reużywalna logika
    Provider
    Globalny stan
    Error Boundary
    Obsługa błędów

    6 wzorców projektowych React

    Compound Components, Custom Hooks, Render Props, HOC, Provider i Error Boundary — kiedy stosować każdy wzorzec i jakie problemy rozwiązuje.

    Wzorzec Cel Implementacja Przykład Kiedy
    Compound Components Elastyczny, powiązany UI Context + Root/Child components Select, Tabs, Accordion, Dialog Komponenty z powiązanym stanem, shadcn/ui
    Custom Hooks Reużywalna logika stanowa useXxx() function useLocalStorage, useDebounce, useFetch Ta sama logika w 2+ komponentach
    Render Props Dzielenie logiki renderowania prop children / render as function react-query renderProp, Formik Rzadko — hooks są lepsze
    HOC Owijanie komponentów withXxx(Component) withAuth, connect (Redux) Class components, biblioteki legacy
    Provider Pattern Globalny stan/DI Context.Provider + useXxx() AuthProvider, ThemeProvider Global state bez prop drilling
    Error Boundary Obsługa błędów renderowania class ErrorBoundary lub react-error-boundary Fallback UI, retry logic Zawsze owijaj sekcje UI

    Często zadawane pytania

    Compound Components Pattern — co to jest i jak implementować?

    Compound Components: wzorzec gdzie kilka komponentów współdzieli ukryty stan przez Context. Przykład z HTML: select + option (natywne compound components). React implementacja: Select.Root, Select.Trigger, Select.Value, Select.Content, Select.Item — jak Radix UI. Implementacja: const SelectContext = createContext(). SelectRoot provider + context. SelectTrigger, SelectItem consumerzy kontekstu. Użycie: Select.Root value={value} onChange={setValue} -> Select.Trigger -> Select.Content -> Select.Item. Zalety: elastyczny layout (user decyduje gdzie co). Brak prop drilling. Czytelna struktura. Wbudowany state management. Przykład Tabs: Tabs.Root + Tabs.List + Tabs.Tab + Tabs.Content. Tabs.Root: activeTab state + context. Tabs.Tab: onClick -> setActiveTab. Tabs.Content: render gdy activeTab === id. shadcn/ui bazuje na tym wzorcu (Radix UI). Wariant z React.Children: React.cloneElement(child, {extraProp}). Mniej elastyczny (hierarchia wymagana). Context lepszy. Implicit state sharing: user nie musi przekazywać state między komponentami. Magicznie się komunikują przez context. Biblioteki: Radix UI, Headless UI — compound components. Reach UI — pierwotny popularyzator wzorca.

    Custom Hooks — jak pisać i kiedy wyodrębniać logikę?

    Custom Hooks: funkcje zaczynające od 'use' korzystające z wbudowanych hooków. Wyodrębniają logikę stanową z komponentów. Cel: reużywalność logiki. Separacja of concerns. Testowalność (hook oddzielnie od UI). Przykłady custom hooks: useLocalStorage: useState + useEffect + localStorage. Przechowuje stan w localStorage. Synchronizacja między tabs (storage event). useDebounce: useState + useEffect z setTimeout. Opóźnia aktualizację wartości. Dla search inputs. useMediaQuery: useState + useEffect z matchMedia. Reaguje na breakpoints. SSR-safe. useFetch: useState + useEffect + AbortController. Generyczny data fetching. Cancel on unmount. useIntersectionObserver: useState + useRef + IntersectionObserver. Lazy loading. Infinite scroll. useEventListener: useEffect z addEventListener. Automatyczne cleanup. Typed events. usePrevious: useRef — przechowuj poprzednią wartość. useClickOutside: useRef + useEventListener. Zamknij dropdown po kliknięciu poza. useHotkeys: keydown event. Skróty klawiszowe. react-hotkeys-hook. Reguły hooków: tylko na top level (nie w if/loop). Tylko w React functions (component lub hook). Kiedy wyodrębniać: ta sama logika w 2+ komponentach. Logika ukrywa szczegóły implementacji. Logika chcesz testować oddzielnie.

    Render Props i Higher-Order Components — stare wzorce vs hooks?

    Render Props (2017): komponent przyjmuje funkcję jako prop. Funkcja zwraca React elements. Pozwala współdzielić logikę stanową. Przykład: MouseTracker render={({x, y}) => div}. Aktualnie: children as function (prop children jako funkcja). Hooks zastąpiły render props dla większości use cases. Ale nadal używane w: react-query (v4 ma render prop API). Formik render prop. Komputacja nad tablicami danych. Higher-Order Components (HOC) (2016): funkcja przyjmuje komponent, zwraca nowy. withAuth(Component) — komponent z auth check. withLoading(Component) — komponent z loading state. Problemy HOC: wrapper hell (wielokrotne owijanie). Nieczytelny displayName. Props collision. Hooks zastąpiły HOC dla większości use cases. Nadal używane: connect() w Redux (stary API). withRouter w React Router 5. HOC dla class components. State Machine HOC (XState) — withMachine. Composition pattern (2024): zamiast HOC używaj kompozycji. ComposedButton = withTracking(withTheme(Button)). Stało się: const Button = () => {const theme = useTheme(); const track = useTracking(); ...}. Provider pattern: Context Provider + custom hook. AuthProvider + useAuth(). ThemeProvider + useTheme(). Container/Presentational (stary): Container — logika. Presentational — UI. Hooks sprawiły że to mniej ważne. Ale nadal wartościowy pattern konceptualnie.

    Performance patterns — memo, useMemo, useCallback i lazy loading?

    React.memo: opakowuje komponent. Rerenderuje tylko gdy props zmieniają się. Domyślne porównanie: shallow equality. Custom: React.memo(Component, (prev, next) => prev.id === next.id). Kiedy używaj: komponent drogi (wiele dzieci, obliczenia). Komponent renderuje się często z tymi samymi props. Kiedy NIE: komponent tani. Props zawsze różne. useMemo: zapamiętuje wynik obliczeń. const sorted = useMemo(() => items.sort(...), [items]). Uwaga: premature optimization. Memoizacja ma koszt. Używaj tylko gdy obliczenie jest drogie. useCallback: zapamiętuje funkcję. Stabilna referencja. Ważne gdy przekazujesz do React.memo dziecka. const handleClick = useCallback(() => {...}, [deps]). React.lazy + Suspense: dynamic import dla code splitting. const HeavyComponent = lazy(() => import('./Heavy')). Suspense fallback={div}. Route-based code splitting (najważniejszy use case). Component-level splitting (mniej potrzebny). startTransition: oznacz update jako non-urgent. Np. search input — wyniki nieistotne do zakończenia typing. isPending z useTransition — loading state. Virtualization: react-window, react-virtual (TanStack Virtual). Renderuj tylko widoczne elementy. 10,000 wierszy listy — tylko 20 renderowanych. React Compiler (RC 2024): automatyczna memoizacja. Zastąpi manualne useMemo/useCallback. React 19 + compiler = mniej boilerplate.

    Error Boundaries i Suspense — obsługa błędów i asynchroniczności?

    Error Boundaries: class components łapiące błędy potomków (tylko class, nie hooks). componentDidCatch(error, info). getDerivedStateFromError(error). Kiedy łapie: render time errors. Constructor errors. Lifecycle method errors. Kiedy NIE łapie: async errors (useEffect, event handlers). SSR errors. Błędy w samym boundary. Implementacja: class ErrorBoundary extends React.Component {state = {hasError: false}. static getDerivedStateFromError() {return {hasError: true}}. render() {if (this.state.hasError) return fallback. return children}}. react-error-boundary: biblioteka ułatwiająca (hooks-friendly). ErrorBoundary + fallbackRender. useErrorBoundary — imperatively throw. resetErrorBoundary — reset po błędzie. Suspense (React 18+): obsługa asynchronicznych zasobów. fallback={Spinner} — podczas ładowania. Działa z lazy (code splitting). Działa z async data (TanStack Query, Relay, SWR — eksperymentalnie). Nested Suspense: każdy ma własny fallback. Granularne loading states. Streaming SSR + Suspense: server streams HTML. Suspense boundaries wysyłane gdy dane gotowe. Szybszy TTFB. use() hook (React 19): use(promise) — zawiesza komponent. use(context) — jak useContext ale może być warunkowy. Async Server Components (Next.js): async function Page() {const data = await fetchData(). return div}. Serwer obsługuje async. Klient dostaje statyczny HTML.

    Czytaj dalej

    Powiązane artykuły

    Kontakt

    Skontaktuj się z nami

    Porozmawiajmy o Twoim projekcie. Bezpłatna wycena w ciągu 24 godzin.

    Wyślij zapytanie

    Bezpłatna wycena w 24h
    Bez zobowiązań
    Indywidualne podejście
    Ekspresowa realizacja

    Telefon

    +48 790 814 814

    Pon-Pt: 9:00 - 18:00

    Email

    adam@fotz.pl

    Odpowiadamy w ciągu 24h

    Adres

    Plac Wolności 16

    61-739 Poznań

    Godziny pracy

    Pon - Pt9:00 - 18:00
    Sob - NdzZamknięte

    Wolisz porozmawiać?

    Zadzwoń teraz i porozmawiaj z naszym specjalistą o Twoim projekcie.

    Zadzwoń teraz