Jeśli jesteś React developerem, na pewno spotkałeś się z pojęciem komponentu wyższego rzędu (ang. Higher-Order Component, HOC).
Jest to ciekawe rozwiązanie, które pozwala współdzielić logikę między komponentami bez potrzeby modyfikowania ich wewnętrznego kodu.
W tym artykule postaram się wyjaśnić, czym jest HOC, jak działa, jakie ma zastosowania oraz jak go zaimplementować.
Omówię także zalety i wady korzystania z komponentów wyższego rzędu w aplikacjach Reactowych.
Co to jest komponent wyższego rzędu (HOC)?
Zacznijmy od podstaw. Komponent wyższego rzędu (ang. Higher-Order Component, HOC) to wzorzec w Reakcie, który polega na tym, że funkcja bierze komponent jako argument i zwraca nowy komponent, który rozszerza ten oryginalny o dodatkową funkcjonalność.
Mówiąc prościej, jest to czysta funkcja, która przekształca jeden komponent w inny.
Można to porównać do dekoratora, który dodaje nowe cechy do komponentu bez zmieniania jego oryginalnego kodu. 💡
Oto przykład prostego komponentu wyższego rzędu:
const withHOC = (WrappedComponent) => {
return (props) => {
return <WrappedComponent {...props} />;
};
};
W powyższym przykładzie funkcja withHOC
przyjmuje jako argument WrappedComponent
(czyli oryginalny komponent) i zwraca nowy komponent, który przekazuje wszystkie props
do oryginalnego komponentu, bez zmiany jego wewnętrznej logiki.
Jak działają komponenty wyższego rzędu?
Aby zrozumieć HOC, warto najpierw zrozumieć, jak działa system props
i renderowania w React.
Komponent wyższego rzędu działa jak "opakowanie", które możesz nałożyć na inne komponenty, by dodać im dodatkową funkcjonalność.
Kluczowe jest to, że HOC nie modyfikuje oryginalnego komponentu, lecz tworzy nowy, zawierający logikę oryginalnego komponentu oraz nowe funkcje.
HOC to funkcje wyższego rzędu, które działają na komponentach w podobny sposób, jak funkcje wyższego rzędu w JavaScript operują na innych funkcjach.
Przykład prostego HOC 🛠️
Przykład HOC, który dodaje prostą funkcjonalność – wyświetlanie komunikatu "Ładowanie..." podczas pobierania danych:
import {useEffect} from 'react
const withLoading = (WrappedComponent) => {
return (props) => {
const [isLoading, setIsLoading] = React.useState(true);
useEffect(() => {
const timer = setTimeout(() => {
setIsLoading(false);
}, 2000); // Symulacja pobierania danych
return () => clearTimeout(timer);
}, []);
if (isLoading) {
return <p>Ładowanie danych...</p>;
}
return <WrappedComponent {...props} />;
};
};
W tym przykładzie withLoading
to funkcja HOC, która przyjmuje komponent jako argument, a następnie zwraca nowy komponent z dodatkowym stanem (isLoading
).
Po zamontowaniu komponentu następuje symulowane pobieranie danych, a po dwóch sekundach komponent jest renderowany z przekazanymi właściwościami (propsami)
.
Dzięki temu HOC możemy łatwo dodać funkcję ładowania do każdego komponentu, bez zmiany jego kodu.
Zastosowania komponentów wyższego rzędu 🚀
Komponenty wyższego rzędu są szczególnie przydatne, gdy chcemy dzielić wspólną logikę między różnymi komponentami. Oto kilka typowych zastosowań:
- Autoryzacja 🔐
W aplikacjach Reactowych niektóre strony mogą być dostępne tylko dla zalogowanych użytkowników.
Możemy utworzyć HOC, który sprawdzi, czy użytkownik jest zalogowany, zanim wyświetli komponent:
const withAuth = (WrappedComponent) => {
return (props) => {
const isAuthenticated = checkIfAuthenticated(); // Funkcja sprawdzająca autoryzację
if (!isAuthenticated) {
return <Redirect to="/login" />;
}
return <WrappedComponent {...props} />;
};
};
-
Logowanie aktywności 📝
Możemy stworzyć HOC, który dodaje funkcję wyświetlania w konsoli aktywności użytkownika:javascriptimport { useEffect } from "react"; const withLogger = (WrappedComponent) => { return (props) => { useEffect(() => { console.log( `Komponent ${WrappedComponent.name} został zaktualizowany` ); }); return <WrappedComponent {...props} />; }; };
-
Wybór motywu 🌈
Możemy stworzyć HOC, który dodaje odpowiednie style w zależności od wybranego motywu aplikacji:javascriptconst withTheme = (WrappedComponent) => { return (props) => { const theme = getTheme(); // Funkcja pobierająca aktualny motyw return <WrappedComponent {...props} theme={theme} />; }; };
Zalety komponentów wyższego rzędu
Dlaczego warto korzystać z HOC w React?
- Reużywalność: HOC pozwala na współdzielenie logiki między komponentami, co redukuje powielanie kodu.
- Modularność: Dzięki oddzieleniu logiki od komponentów, możemy tworzyć bardziej modularny i skalowalny kod.
- Elastyczność: HOC może przyjmować dodatkowe argumenty, co pozwala dostosować jego działanie do różnych przypadków użycia.
Wady komponentów wyższego rzędu 🤔
Jednym z problemów może być tzw. "wrapper hell" – gdy zbyt wiele HOC zostanie nałożonych na jeden komponent, może to utrudnić debugowanie i śledzenie przepływu propsów
.
Może to również negatywnie wpłynąć na czytelność kodu.
HOC vs React Hooks 🔄
Od wprowadzenia Hooków w Reakcie, wiele przypadków użycia HOC można zastąpić hookami, które są bardziej intuicyjne i łatwiejsze do zrozumienia.
Przykładem jest zastąpienie HOC do zarządzania stanem ładowania hookiem useState
:
import { useEffect } from "react";
function useLoadingData() {
const [isLoading, setIsLoading] = React.useState(true);
useEffect(() => {
const timer = setTimeout(() => setIsLoading(false), 2000);
return () => clearTimeout(timer);
}, []);
return isLoading;
}
Jeśli jesteś zainteresowany to mam jeszcze inny artykuł na temat Pobierania danych w React za pomocą wbudowanych hooków
Dzięki hookom możemy współdzielić logikę bez konieczności tworzenia nowych komponentów.
Jednak HOC wciąż mają swoje miejsce w React, szczególnie w przypadku bardziej zaawansowanych funkcji, takich jak autoryzacja czy logowanie aktywności użytkowników.
Podsumowanie
Komponenty wyższego rzędu (HOC) to ciekawe rozwiązanie, które pozwala na współdzielenie logiki między różnymi komponentami bez ich modyfikacji.
Pozwalają na dodawanie dodatkowych funkcji, zwiększając modularność i elastyczność kodu.
Warto jednak pamiętać, aby stosować HOC z umiarem, by uniknąć nadmiernego skomplikowania aplikacji.
W prostszych przypadkach warto rozważyć użycie Reactowych Custom Hooków, które oferują nowoczesne podejście do współdzielenia logiki w komponentach funkcyjnych.
Spróbuj stworzyć komponent wyższego rzędu i zobacz, jak bardzo może ułatwić Twoją pracę