Wzorce projektowe React
Compound Components, Custom Hooks, Provider Pattern i Error Boundaries — najważniejsze wzorce dla skalowalnych aplikacji React.
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.
Powiązane artykuły
Skontaktuj się z nami
Porozmawiajmy o Twoim projekcie. Bezpłatna wycena w ciągu 24 godzin.
Wyślij zapytanie
Telefon
+48 790 814 814
Pon-Pt: 9:00 - 18:00
adam@fotz.pl
Odpowiadamy w ciągu 24h
Adres
Plac Wolności 16
61-739 Poznań
Godziny pracy
Wolisz porozmawiać?
Zadzwoń teraz i porozmawiaj z naszym specjalistą o Twoim projekcie.
Zadzwoń teraz