Context API vs Redux — co wybrać?
Co jest lepsze, Context API , czy Redux w projektach React? Odpowiedź na to pytanie nie jest jednoznaczna, choć często się słyszy, że Redux częściej stosuje się w złożonych projektach. Od czego to zależy?
Przede wszystkim trzeba zacząć od tego, w jaki sposób wygląda zarządzanie stanem komponentów w obu technologiach. Jakie narzędzia do tego służą oraz w jaki sposób dystrybuujemy dane w Context API i Redux? Przyjrzyjmy się bliżej tym zagadnieniom.
Spis treści
Czym jest zarządzanie stanem aplikacji?
Stan aplikacji to miejsce przechowywania kodu. To część wspólna wszystkich widoków. Na podstawie tych danych React renderuje aplikację, a w przypadku, jeśli dane się zmienią, to React zadba o to, żeby dokonała się aktualizacja.
Zarządzanie stanem front-endu to swego rodzaju logika, która przechowuje i odświeża aktualne dane, takie jak informacje o podświetleniu przycisku opcji, o autoryzacji użytkownika itp. Albo, ujmując to bardziej abstrakcyjnie, zapewnia kompletność transakcji biznesowych – przechowując dane wejściowe interfejsu użytkownika i synchronizując je między stronami, częściami back-end i front-end.
W tym artykule omówimy główne różnice w zarządzaniu stanem aplikacji, wykorzystując konteksty lub Redux. Jednak zaczniemy od tego, w jaki sposób w React dystrybuuje się dane.
W jaki sposób można dystrybuować dane?
Najprostszym sposobem przekazywania danych od rodzica do dziecka w aplikacji React jest przekazanie ich do dziecka przez propsy, czyli argumenty przekazywane do komponentów jako obiekty.
Props Drilling
W przypadku modułu, który jest rozbudowany i ma wiele sekcji, to stosując technikę „props drilling” przekazujemy sobie dane od góry modułu do któregoś elementu w środku. Każda zmiana stanu bądź propsów w React powoduje jego “rerender”, czyli ponowne wyrenderowanie zawartości np. mając “to do” listę, która ma 5 itemów, dodajemy 6 item do tej listy. Wtedy React z racji, że zmieniliśmy props „list” przerenderuje całą listę jeszcze raz. Gdy stosujemy props drilling, to każda zmiana propsa do góry powoduje przerenderowanie komponentu, któremu przekazany jest ten props.
Czas przed Reactem 16.3 – zanim twórcy Reacta stworzyli Context API w praktyce.
Na grafice widać typowy szkielet aplikacji React. “Article” jest renderowany przez „Content”, „Content” przez „Main”, „Main” przez „App”’ a „State” jest podpięte do aplikacji.
Zanim twórcy React wprowadzili Context API sytuacja wyglądała tak, że mając określony „State” aplikacji i chcąc przekazać go dalej do „Article” musieliśmy go przekazywać kolejno przez propsy.
Jednak problem pojawia się, gdy głęboko zagnieżdżony element potomny wymaga danych z komponentu znajdującego się wyżej w drzewie. Jeśli chcemy, żeby kod był czytelniejszy, a aplikację można było łatwiej modyfikować niekiedy lepiej zrezygnować z “props drilling”. Powinno się unikać przekazywania propsów na więcej niż dwa, trzy poziomy. Jest to ważne, aby później przy zmianie drzewa komponentów aplikacji nie wprowadzić zamieszania, nie wiedząc, jaki props przekazywaliśmy do danego komponentu.
Aby rozwiązać problem props drilling, mamy rozwiązania do zarządzania stanem, takie jak Context API i Redux.
Czym jest Context API?
Context API to wbudowane narzędzie React, które nie wpływa na ostateczny rozmiar pakietu i jest z założenia zintegrowane. Jest to rozwiązanie zaprojektowane do dzielenia się stanem aplikacji, czyli jest to coś, co pozwala nam zarządzać stanem naszej aplikacji nie tylko przez przekazywanie propsów w dół, ale przez tworzenie pewnych kontenerów, które trzymają nasz stan i umożliwiają przekazywanie stanu w dół w drzewie komponentów do dowolnego miejsca w aplikacji.
W Context API sami tworzymy sposób aktualizacji i pobierania “state”. Context API pozwala na dzielenie się danych w ramach jednego modułu między jego komponentami.
Jakakolwiek zmiana stanu w komponencie, który używa lokalnie “useState”, bądź zmiana w kontekście, który komponent subskrybuje, powoduje ponowny rerender tego komponentu — dlatego raczej powinny się w nim zawierać dane statyczne.
Na czym polega Context API?
Tworzymy sobie kontekst, czyli miejsce do przechowywania stanu i przekaźnik stanu do innych komponentów w postaci „providerów”.
Kontekst pozwala nam niejako „opleść” komponenty, które chcemy, by miały dostęp do danej rzeczy i w bardzo łatwy sposób przekazywać dane wartości, stan niżej w drzewie komponentów. Kontekst API pozwala czytelniej zarządzać drzewem aplikacji, przekazując dane przez drzewo komponentów bez konieczności ręcznego przekazywania rekwizytów na każdym poziomie.
Możemy mieć kilka kontekstów, które osobno odpowiadają za oddzielne kwestie. To pozwala nam kontrolować, w jaki sposób zachowuje się nasza aplikacja, kiedy aplikacja się renderuje, gdy pewne dane się zmieniają.
Kiedy stosować Context API?
Są dwie sytuacje, kiedy warto to stosować. Między innymi, gdy:
- Dane muszą być dostępne w różnych komponentach na wielu poziomach zagnieżdżenia. Zawsze warto zadać sobie pytanie: czy dane, których potrzebuję, muszą być dostępne w różnych komponentach i na różnych poziomach.
- Nie ma możliwości innej konfiguracji drzewa komponentów, by uniknąć „globalnego” stanu”. Czasami wystarczy sama zmiana kompozycji komponentów, samo przeniesienie stanu wyżej bądź niżej by rozwiązać dany problem w aplikacji.
Context API — przykład użycia
Poniżej pokazujemy stworzenie przykładowego kontekstu dla „user”. Mamy w nim informacje o nazwie użytkownika (username), dostępach, grupach, wybranym motywie, czy też o jego ograniczeniach bądź wymaganiach dot. zmiany hasła.
Redux — czym się charakteryzuje?
Redux to otwarta biblioteka JavaScript do zarządzania stanem, który wprowadza swoją architekturę Flux. Najlepiej sprawdza się w rozbudowanych aplikacjach. To, czym jest Redux można zobrazować przykładem. Przypomina niejako dużą przesuwną szafę. Zawarte jest w niej wszystko, co dotyczy aplikacji. Jest ona podzielona w środku na półki, ale ten, kto obserwuje zmiany w Redux obserwuje całą szafę, nie jedną półkę.
Redux działa w całej aplikacji i umożliwia wskazanie tego “co” i z “której półki” chcemy czytać. W ten sposób z każdego komponentu można mieć dostęp do „store”. Działa to płynnie, o ile oprogramowanie pośrednie jest gotowe i mamy pełną kontrolę nad pobieraniem, stanami itp. Z drugiej strony nie zawsze jest ono wydajne, bo jakakolwiek zmiana w „store” powoduje, że wszystko się odświeża, powodując jego wolniejsze działanie.
Z punktu widzenia architektury, Redux pomaga utrzymać porządek w folderach i plikach projektu. Ułatwia programistom zrozumienie struktury aplikacji i wprowadzenie do projektu nowych osób (pod warunkiem wcześniejszej znajomości Redux). Jego składowymi są: globalny store, reducery, akcje i rozszerzenia, które działają postaci oprogramowania middleware. Więcej na ten temat pisaliśmy w poniższym artykule
Czy da się kodować w React bez Redux’a?
Oczywiście, że się da. Jedyny problem to fakt, ze Redux wyręczał nas naprawdę w wielu rzeczach więc w tym momencie będziemy sami odpowiedzialni za dystrybucję danych w drzewie komponentów React. To powoduje, że będziemy musieli pamiętać o kwestiach performance’owych.
Kiedy zastosowanie ma Redux, a kiedy Context API?
To wszystko zależy od specyficznego przypadku użycia. Zarówno Redux, jak i Context API pomagają uniknąć przekazywania kolejno propsów w drzewie komponentów oraz pozwalają wpinać się bezpośrednio komponentem do drzewa aplikacji.
▶️ Context API
Context API dystrybuuje dane bardziej lokalnie. Najlepiej nadaje się do operacji na danych, w których występują aktualizacje stanu o wysokiej częstotliwości (wszystko być zdefiniowane lokalnie w “useState”).
Kontekst jest tylko mechanizmem udostępniania wartości w zagnieżdżonym poddrzewie komponentów, a nie podejściem do samego zarządzania stanem. Oznacza to, że musisz napisać dowolną logikę zarządzania stanem potrzebną do zdefiniowania wartości przesyłanej do dostawcy kontekstu. Na przykład przydaje się do pisania poszczególnych modułów takich jak router czy obsługa formularzy, które są hermetyczne i łatwe do testowania. Natomiast sam context API nie zarządza stanem aplikacji.
Cechy Context API:
- Wbudowane narzędzie dostarczane z React
- Wymaga minimalnej konfiguracji
- Działa na danych statycznych, które nie są często odświeżane ani aktualizowane
- Dodawanie nowych kontekstów wymaga tworzenia ich od podstaw
- Debugowanie może być trudne w wysoce zagnieżdżonej strukturze komponentów React, nawet z narzędziem deweloperskim
- Logika interfejsu użytkownika i logika zarządzania stanem znajdują się w tym samym komponencie
▶️ Redux
Redux wymaga dodania większej liczby bibliotek do pakietu aplikacji. Wykorzystuje się go, wtedy gdy na przykład chcemy przeprowadzać modyfikacje z odległych komponentów, komunikować je między sobą lub dzielić ich stan. Zadaniem Redux’a jest trzymanie stanu — renderuje aplikację React, a po pierwszym wyrenderowaniu mówi się o “rerenderze”.
Cechy Redux
- Wymagana dodatkowa instalacja, zwiększająca ostateczny rozmiar pakietu
- Wymaga rozbudowanej konfiguracji, aby zintegrować go z aplikacją React
- Działa jak zarówno z danymi statycznymi, jak i dynamicznymi
- Łatwo dodawać nowe dane/działania po początkowej konfiguracji
- Potężne narzędzia Redux Dev Tools ułatwia debugowanie
- Ułatwia organizację kodu, zapewniając oddzielną logikę interfejsu użytkownika i logikę zarządzania stanem
Podsumowanie
Decydując się na wybór rozwiązania do zarządzania stanem aplikacji, najpierw trzeba oszacować rozmiar swojego projektu. Jest to ważne, żeby w miarę powiększania się aplikacji nie trzeba było jej przepisać całej z Context na Redux. Warto też wiedzieć, które dane wymagają odświeżania i jak często to robić. Jeśli pracujemy nad większym, złożonym przedsięwzięciem, najczęściej wybieramy Redux do globalnego zarządzania stanem. Jeśli projekt jest mniejszy bądź skupiamy się na hermetyzacji modułów warto skorzystać z Context API.
W Blurify współpracujemy z Tobą w ramach rozwoju aplikacji mobilnych i webowych. Nasz zespół jest na bieżąco z nowinami technologicznymi, dzięki czemu pomożemy Ci wybrać profesjonalne rozwiązania dla Twojego projektu. Skontaktuj się z nami, a doradzimy Ci w rozwoju Twojego produktu.