Artykuły | 13 kwiecień, 2023

Jeśli nie Redux, to co? Zarządzanie stanem aplikacji w React

Większość starszych projektów wykorzystujących React korzysta z biblioteki Redux. To popularne rozwiązanie, które przyzwyczaiło programistów do określonego sposobu działania. W dodatku ma sporą społeczność użytkowników gotowych służyć pomocą. Jeśli pracujesz w nowym projekcie, w którym priorytetem jest szybkość wdrażania i ograniczenie ilości kodu, warto wziąć pod uwagę inne opcje. W artykule przedstawiam kilka intuicyjnych i łatwych w obsłudze narzędzi, które z powodzeniem mogą konkurować z biblioteką Redux. Które wybrać? Sprawdź!

Jeśli nie Redux, to co? Zarządzanie stanem aplikacji w React

Zarządzanie stanem w React

Ktoś stworzył aplikację z przyciskiem. Wciskasz go i za każdym razem wyświetla się inna wartość. Pewnie zastanawiasz się, co dzieje się „pod spodem”. To albo czary, albo… zarządzanie stanem aplikacji. Tym razem zajmiemy się tym drugim tematem. Zarządzanie stanem w React to proces kontrolowania i aktualizowania danych w aplikacji.

Redux

Jest jednym z kluczowych zagadnień przy pracy z biblioteką programowania JavaScript. Stan to taka wartość, która zmienia się na przykład na skutek interakcji użytkownika z aplikacją. W React stan zazwyczaj przechowywany jest w komponentach, a jego zmiana może powodować ponowne renderowanie komponentu lub całego drzewa komponentów. Zarządzanie stanem jest dosyć istotne. Pozwala nam tworzyć dynamiczne i interaktywne aplikacje – dzięki temu aplikacja może:

  • dostosowywać się do zmieniającego się stanu,
  • reagować na interakcję użytkownika,
  • wykonywać pewne akcje w odpowiednich momentach.

Jest to ważne w przypadku złożonych aplikacji, w których wiele komponentów zależy od stanów w innych komponentach.

Czym jest Redux i jak działa?

Zapewne większość osób zaznajomionych z React, słysząc o zarządzaniu stanem, przed oczami ma tylko jedno – Redux.

Redux jest biblioteką służącą do zarządzania stanem aplikacji w React. Jest to narzędzie, które umożliwia tworzenie bardziej przewidywalnych i łatwiejszych do testowania aplikacji poprzez uproszczenie zarządzania stanem i zachowywania go w jednym miejscu. Redux wprowadza wiele pojęć, które pozwalają na bardziej efektywne i przewidywalne zarządzanie stanem. Jednym z nich jest jednostronny przepływ danych. Stan aplikacji jest przechowywany w jednym centralnym magazynie, tzw. store. Dane między store a komponentami przepływają w odpowiedni sposób, zgodny z architekturą Flux. Zmiany w stanie są dokonywane tylko za pomocą akcji, czyli prostych obiektów, które opisują zmiany, jakie mają być wykonane w stanie aplikacji.

Warto również wspomnieć, że Redux może działać niezależnie od Reacta. Biblioteka ta jest uniwersalna i może być używana w innych aplikacjach internetowych. Jeśli ktoś jednak poszukuje nieskomplikowanego, intuicyjnego narzędzia, to warto poznać też inne dostępne opcje.

import React from "react";
import { Provider, useDispatch, useSelector } from "react-redux";
import { createStore } from "redux";


const incrementAction = () => ({ type: "INCREMENT" });
const decrementAction = () => ({ type: "DECREMENT" });


const counterReducer = (state = 0, action) => {
 switch (action.type) {
   case "INCREMENT":
     return state + 1;
   case "DECREMENT":
     return state - 1;
   default:
     return state;
 }
};


const store = createStore(counterReducer);


const Counter = () => {
 const count = useSelector((state) => state);
 const dispatch = useDispatch();


 return (
   <div>
     <div>Count: {count}</div>
     <button onClick={() => dispatch(incrementAction())}>+</button>
     <button onClick={() => dispatch(decrementAction())}>-</button>
   </div>
 );
};


const CounterApp = () => {
 return (
   <Provider store={store}>
     <Counter />
   </Provider>
 );
};

Wykorzystanie innych rozwiązań

Na szczęście istnieją różne sposoby zarządzania stanem aplikacji w React:

Context API

To wbudowane w React narzędzie do przekazywania danych między komponentami bez konieczności przekazywania ich przez „propsy” (właściwości, ang. properties). Dzięki temu można przekazywać dane do komponentów znajdujących się niżej w hierarchii bezpośrednio z góry. Można to wykorzystać do zarządzania stanem aplikacji, przekazując stan do całej aplikacji lub tylko do części komponentów.

import React, { createContext, useContext, useState } from "react";


const CounterContext = createContext();


const CounterProvider = ({ children }) => {
 const [counter, setCounter] = useState(0);


 const increment = () => {
   setCounter((prevCounter) => prevCounter + 1);
 };


 const decrement = () => {
   setCounter((prevCounter) => prevCounter - 1);
 };


 return (
   <CounterContext.Provider value={{ counter, increment, decrement }}>
     {children}
   </CounterContext.Provider>
 );
};


const CounterDisplay = () => {
 const { counter } = useContext(CounterContext);


 return <div>Counter: {counter}</div>;
};


const CounterButtons = () => {
 const { increment, decrement } = useContext(CounterContext);


 return (
   <div>
     <button onClick={increment}>+</button>
     <button onClick={decrement}>-</button>
   </div>
 );
};


const CounterApp = () => {
 return (
   <CounterProvider>
     <CounterDisplay />
     <CounterButtons />
   </CounterProvider>
 );
};

MobX

To biblioteka, która umożliwia łatwe zarządzanie stanem aplikacji w React. W przeciwieństwie do Redux – MobX jest bardziej elastyczny i wymaga mniej kodu. Używa się go do przechowywania stanu w obiektach, które reprezentują dane aplikacji. MobX automatycznie aktualizuje widok, gdy dane zmienią się w magazynie.

import React from "react";
import { observable } from "mobx";
import { observer } from "mobx-react";

const counterStore = observable({
 counter: 0,
 increment() {
   this.counter++;
 },
 decrement() {
   this.counter--;
 },
});

const CounterDisplay = observer(() => <div>Counter: {counterStore.counter}</div>);

const CounterButtons = observer(() => (
 <div>
   <button onClick={() => counterStore.increment()}>+</button>
   <button onClick={() => counterStore.decrement()}>-</button>
 </div>
));

const CounterApp = () => {
 return (
   <div>
     <CounterDisplay />
     <CounterButtons />
   </div>
 );
};

RxJS

To biblioteka reaktywna do zarządzania asynchronicznymi strumieniami danych. Można jej używać do zarządzania stanem w aplikacji. RxJS pozwala na łatwe reagowanie na zmiany w danych i wykonywanie akcji na podstawie tych zmian.

import React, { useState, useEffect } from "react";
import { Subject } from "rxjs";
import { scan } from "rxjs/operators";

const counterSubject = new Subject();

const counter$ = counterSubject.pipe(
 scan((count, operation) => {
   if (operation === "+") {
     return count + 1;
   } else if (operation === "-") {
     return count - 1;
   } else {
     return count;
   }
 }, 0)
);

const CounterDisplay = () => {
 const [counter, setCounter] = useState(0);


 useEffect(() => {
   const subscription = counter$.subscribe(setCounter);
   return () => subscription.unsubscribe();
 }, []);


 return <h1>Counter: {counter}</h1>;
};


const CounterButtons = () => {
 const handleButtonClick = (operation) => {
   counterSubject.next(operation);
 };


 return (
   <div>
     <button onClick={() => handleButtonClick("+")}>+</button>
     <button onClick={() => handleButtonClick("-")}>-</button>
   </div>
 );
};

const CounterApp = () => {
 return (
   <div>
     <CounterDisplay />
     <CounterButtons />
   </div>
 );
};

Recoil

Recoil to biblioteka, która działa poprzez przechowywanie stanu aplikacji w tzw. atomach. Atomy są prostymi obiektami, które zawierają aktualną wartość stanu oraz funkcje, które pozwalają na aktualizację wartości. Dzięki temu łatwo jest zarządzać stanem aplikacji i aktualizować go w reakcji na interakcję podejmowaną przez użytkownika lub inne zdarzenia.

import React from "react";
import { useRecoilState, atom, RecoilRoot } from "recoil";


const counterState = atom({
 key: "counterState",
 default: 0,
});

const CounterDisplay = () => {
 const [counter] = useRecoilState(counterState);

 return <div>Counter: {counter}</div>;
}

const CounterButtons = () => {
 const [counter, setCounter] = useRecoilState(counterState);


 const increment = () => {
   setCounter(counter + 1);
 };

 const decrement = () => {
   setCounter(counter - 1);
 };

 return (
   <div>
     <button onClick={increment}>+</button>
     <button onClick={decrement}>-</button>
   </div>
 );
}

const CounterApp = () => {
 return (
   <RecoilRoot>
     <CounterDisplay />
     <CounterButtons />
   </RecoilRoot>
 );
};

Zustand

Zustand to lekka biblioteka do zarządzania stanem w React, która wykorzystuje podstawowe funkcjonalności React do przechowywania i aktualizowania stanu. Oferuje proste API i pozwala na tworzenie globalnego stanu, zarządzanie stanem w komponentach, własnych modułach, tworzenie selektorów i subskrypcji stanu. Jest szybka i wydajna, co sprawia, że działa dobrze nawet w dużych aplikacjach.

import React from "react";
import create from "zustand";

const useCounterStore = create((set) => ({
 counter: 0,
 incrementCounter: () => set((state) => ({ counter: state.counter + 1 })),
 decrementCounter: () => set((state) => ({ counter: state.counter - 1 })),
}));

const CounterDisplay = () => {
 const counter = useCounterStore((state) => state.counter);

 return <div>Counter: {counter}</div>;
}

const CounterButtons = () => {
 const { incrementCounter, decrementCounter } = useCounterStore();

 return (
   <div>
     <button onClick={incrementCounter}>+</button>
     <button onClick={decrementCounter}>-</button>
   </div>
 );
}

const CounterApp = () => {
 return (
   <div>
     <CounterDisplay />
     <CounterButtons />
   </div>
 );
};

Które rozwiązanie wybrać?

Wybór rozwiązania do zarządzania stanem w React zależy od wielu czynników, takich jak rozmiar i złożoność projektu, preferencje zespołu programistycznego, doświadczenia z innymi bibliotekami i podejściami. Większość starszych projektów w React korzysta z biblioteki Redux, gdyż było to najpopularniejsze rozwiązanie, które przyzwyczaiło programistów do swojego sposobu działania. Redux jest bardzo popularny wśród większych projektów i ma duże wsparcie społeczności, co może pomóc w rozwiązywaniu problemów – nie jest jednak polecany w nowych projektach.

Według mnie, jeśli rozpoczynasz nowy projekt w React i potrzebujesz rozwiązania do zarządzania stanem, to sugerowałbym rozważenie Recoil lub Zustand. Są to biblioteki, które zostały opracowane z myślą o prostocie i łatwości użycia, co może być szczególnie ważne w nowym projekcie, w którym chcesz skupić się na szybkim wdrożeniu funkcjonalności i ograniczeniu ilości kodu. Recoil i Zustand pozwalają na tworzenie stanu przy użyciu hooków, co ułatwia integrację z React i zmniejsza potrzebę tworzenia dodatkowych plików. W przypadku Recoil możesz również korzystać z selektorów, które pozwalają na pobieranie tylko niezbędnych danych ze stanu, co może przyspieszyć renderowanie aplikacji.

Zarządzanie stanem aplikacji – podsumowanie

Podsumowując, wybór biblioteki do zarządzania stanem w React powinien być solidnie przeanalizowany i nie jest łatwo odpowiedzieć na pytanie „które narzędzie wybrać?”. Jeśli priorytetem są łatwość użycia i szybka implementacja funkcjonalności, dobrym pomysłem może być wykorzystanie któregoś z innych rozwiązań – na przykład Recoil lub Zustand.

Jeśli zastanawiasz się nad wyborem Reduxa, to warto jest przeanalizować, czy projekt jest na tyle złożony i wymagający, że jego zastosowanie jest potrzebne. Może się też okazać, że projekt nie wymaga użycia żadnej biblioteki do zarządzania stanem, ponieważ sam React dostarcza narzędzi do zarządzania stanem w komponentach, które są wystarczające dla prostych projektów

Zapisz się do newslettera, ekskluzywna zawartość czeka

Bądź na bieżąco z najnowszymi artykułami i wydarzeniami IT

Informacje dotyczące przetwarzania danych osobowych

Zapisz się do newslettera, ekskluzywna zawartość czeka

Bądź na bieżąco z najnowszymi artykułami i wydarzeniami IT

Informacje dotyczące przetwarzania danych osobowych

Zapisz się do newslettera, aby pobrać plik

Bądź na bieżąco z najnowszymi artykułami i wydarzeniami IT

Informacje dotyczące przetwarzania danych osobowych

Dziękujemy za zapis na newsletter — został ostatni krok do aktywacji

Potwierdź poprawność adresu e-mail klikając link wiadomości, która została do Ciebie wysłana w tej chwili.

 

Jeśli w czasie do 5 minut w Twojej skrzynce odbiorczej nie będzie wiadomości to sprawdź również folder *spam*.

Twój adres e-mail znajduje się już na liście odbiorców newslettera

Wystąpił nieoczekiwany błąd

Spróbuj ponownie za chwilę.

    Get notified about new articles

    Be a part of something more than just newsletter

    I hereby agree that Inetum Polska Sp. z o.o. shall process my personal data (hereinafter ‘personal data’), such as: my full name, e-mail address, telephone number and Skype ID/name for commercial purposes.

    I hereby agree that Inetum Polska Sp. z o.o. shall process my personal data (hereinafter ‘personal data’), such as: my full name, e-mail address and telephone number for marketing purposes.

    Read more

    Just one click away!

    We've sent you an email containing a confirmation link. Please open your inbox and finalize your subscription there to receive your e-book copy.

    Note: If you don't see that email in your inbox shortly, check your spam folder.