Komponenty wyższego rzędu (HOC) w React

React

readTime

5 min

Komponenty wyższego rzędu (HOC) w React

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:

javascript
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:

javascript
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ń:

  1. 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:

javascript
const withAuth = (WrappedComponent) => {
  return (props) => {
    const isAuthenticated = checkIfAuthenticated(); // Funkcja sprawdzająca autoryzację

    if (!isAuthenticated) {
      return <Redirect to="/login" />;
    }

    return <WrappedComponent {...props} />;
  };
};
  1. Logowanie aktywności 📝
    Możemy stworzyć HOC, który dodaje funkcję wyświetlania w konsoli aktywności użytkownika:

    javascript
    import { useEffect } from "react";
    
    const withLogger = (WrappedComponent) => {
      return (props) => {
        useEffect(() => {
          console.log(
            `Komponent ${WrappedComponent.name} został zaktualizowany`
          );
        });
    
        return <WrappedComponent {...props} />;
      };
    };
    
  2. Wybór motywu 🌈
    Możemy stworzyć HOC, który dodaje odpowiednie style w zależności od wybranego motywu aplikacji:

    javascript
    const 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:

javascript
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ę

authorImg

Witek Pruchnicki

Z pasją dzielę się wiedzą o programowaniu i nie tylko na różne sposoby