Najlepsze narzędzia do zarządzania stanem React dla aplikacji korporacyjnych

Opublikowany: 2022-03-11

Deweloperzy aplikacji React na poziomie korporacyjnym wiedzą, jak kluczowe znaczenie dla spójnego doświadczenia użytkownika końcowego ma zarządzanie stanem.

Jednak nie tylko użytkownik ma wpływ na zarządzanie państwem. Deweloperzy React tworzą i utrzymują stan. Chcą, aby zarządzanie stanem było proste, rozszerzalne i atomowe. React poszedł w tym kierunku, wprowadzając hooki.

Problemy mogą się pojawić, gdy stan powinien być dzielony między wiele komponentów. Inżynierowie muszą znaleźć narzędzia i biblioteki, które odpowiadają ich potrzebom, a jednocześnie spełniają wysokie standardy wymagane dla aplikacji klasy korporacyjnej.

W tym artykule przeanalizuję i porównam najpopularniejsze biblioteki oraz wybiorę najbardziej odpowiednią do zarządzania stanem w aplikacji na poziomie przedsiębiorstwa.

Wbudowane możliwości zarządzania stanem reakcji

React ma doskonałe narzędzie do dostarczania danych z wielu komponentów. Podstawowym celem Context jest unikanie drążenia rekwizytów. Naszym celem jest uzyskanie łatwego w użyciu narzędzia do zarządzania stanem w różnych scenariuszach, które można napotkać w aplikacjach korporacyjnych: częste aktualizacje, przeprojektowanie, wprowadzanie nowych funkcji i tak dalej.

Chociaż wszystko to jest teoretycznie możliwe do wykonania za pomocą Context, wymagałoby to niestandardowego rozwiązania, które wymaga czasu na konfigurację, obsługę i optymalizację. Jedyną zaletą Context jest to, że nie jest on zależny od biblioteki innej firmy, ale nie może to przewyższyć wysiłku związanego z utrzymaniem tego podejścia.

Ponadto członek zespołu React, Sebastian Markbage, wspomniał, że nowe API kontekstowe nie zostało zbudowane i zoptymalizowane pod kątem aktualizacji o wysokiej częstotliwości, ale raczej dla aktualizacji o niskiej częstotliwości, takich jak aktualizacje motywów i zarządzanie uwierzytelnianiem.

Badanie istniejących bibliotek

W serwisie GitHub znajdują się dziesiątki narzędzi do zarządzania stanem (np. Redux, MobX, Akita, Recoil i Zustand). Jednak uwzględnienie każdego z nich prowadziłoby do niekończących się badań i porównań. Dlatego zawęziłem mój wybór do trzech głównych konkurentów na podstawie ich popularności , użytkowania i opiekuna .

Aby porównanie było jednoznaczne, użyję następujących atrybutów jakości:

  • Użyteczność
  • Utrzymywalność
  • Występ
  • Testowalność
  • Skalowalność (działa z taką samą wydajnością na większych stanach)
  • Modyfikowalność
  • Możliwość ponownego wykorzystania
  • Ekosystem (posiada różnorodne narzędzia pomocnicze do rozszerzenia funkcjonalności)
  • Społeczność (ma wielu użytkowników, a odpowiedzi na ich pytania znajdują się w sieci)
  • Przenośność (może być używany z bibliotekami/frameworkami innymi niż React)

Redux

Redux to kontener stanu stworzony w 2015 roku. Stał się szalenie popularny, ponieważ:

  • W momencie premiery nie było żadnej poważnej alternatywy.
  • Zapewniał oddzielenie państwa od działań.
  • react-redux magia umożliwiła proste połączenie stanu.
  • Współtwórcą biblioteki jest uznany programista Facebooka i członek głównego zespołu React, Dan Abramov.

Animacja przedstawiająca progresję stanów i akcji od i do reduktora przy użyciu Redux.

Masz globalny sklep, w którym znajdują się Twoje dane. Za każdym razem, gdy potrzebujesz zaktualizować sklep, wysyłasz akcję , która trafia do reduktora . W zależności od typu akcji reduktor aktualizuje stan w sposób niezmienny.

Aby używać Redux z React, musisz zasubskrybować komponenty do aktualizacji sklepu za pośrednictwem react-redux .

Przykład interfejsu API Redux

Podstawowymi częściami Redux w bazie kodu, które odróżniają go od innych narzędzi, są wycinki. Zawierają całą logikę działań i reduktorów.

CodeSandbox

 // slices/counter.js import { createSlice } from "@reduxjs/toolkit"; export const slice = createSlice({ name: "counter", initialState: { value: 0 }, reducers: { increment: (state) => { state.value += 1; }, decrement: (state) => { state.value -= 1; } } }); export const actions = slice.actions; export const reducer = slice.reducer; // store.js import { configureStore } from "@reduxjs/toolkit"; import { reducer as counterReducer } from "./slices/counter"; export default configureStore({ reducer: { counter: counterReducer } }); // index.js import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import App from './App' import store from './store' ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ) // App.js import React from "react"; import { useSelector, useDispatch } from "react-redux"; import { actions } from "./slices/counter"; const App = () => { const count = useSelector((state) => state.counter.value); const dispatch = useDispatch(); return ( <div> <div> <button onClick={() => dispatch(actions.increment())}>Increment</button> <span>{count}</span> <button onClick={() => dispatch(actions.decrement())}>Decrement</button> </div> </div> ); }; export default App;

Atrybuty jakości

  • Użyteczność . Redux stał się bardzo prosty dzięki wprowadzeniu oficjalnego pakietu narzędzi. Tworzysz plasterek (kombinację stanu początkowego, reduktorów i akcji), przekazujesz go do sklepu i uzyskujesz do niego dostęp w komponencie za pomocą zaczepów.
  • Utrzymanie . Redux jest prosty. Nie wymaga głębokiej wiedzy, aby zrozumieć, jak coś ulepszyć lub naprawić.
  • Wydajność . Głównym czynnikiem wpływającym na wydajność w Redux jest inżynier oprogramowania. Redux to proste narzędzie bez dużej logiki. Jeśli zauważysz, że aktualizacje stanu są powolne, możesz postępować zgodnie z oficjalnymi wytycznymi, aby je przyspieszyć.
  • Testowalność . Redux składa się z czystych funkcji (akcji i reduktorów), dzięki czemu świetnie nadaje się do testowania jednostkowego. Zapewnia również mechanizm pisania testów integracyjnych, w których sklep, akcje i reduktory współpracują ze sobą.
  • Skalowalność . Domyślnie Redux ma jeden stan globalny, co utrudnia skalowanie. Istnieje jednak biblioteka redux-dynamic-modules, która umożliwia tworzenie modułowych reduktorów i oprogramowania pośredniczącego.
  • Modyfikowalność . Dostosowywanie Redux jest sprawą łatwą, ponieważ obsługuje oprogramowanie pośredniczące.
  • Możliwość ponownego wykorzystania . Redux jest niezależny od frameworka, więc jest bardzo dobry w ponownym użyciu.
  • Ekosystem . Redux oferuje gigantyczny ekosystem pomocnych dodatków, bibliotek i narzędzi.
  • Wspólnota . Redux, najstarsza państwowa biblioteka zarządzania w naszym porównaniu, zgromadziła dużą społeczność z pokaźną bazą wiedzy. Na Stack Overflow znajduje się ~ 30 000 (~ 19 000 odpowiedzi) pytań z tagiem redux .
  • Puls . Redux jest regularnie aktualizowany i utrzymywany.

MobX

MobX to kolejna stosunkowo stara biblioteka z 23 000 gwiazdek na GitHub. Tym, co odróżnia go od Redux, jest to, że podąża za paradygmatem OOP i wykorzystuje obserwable. MobX został stworzony przez Michela Weststrate i jest obecnie utrzymywany przez grupę entuzjastów open-source z pomocą bostońskiego Mendixa.

Diagram przedstawiający zarządzanie stanami za pomocą MobX, od akcji, poprzez obserwowalne stany i wartości wyliczane, po efekty uboczne.

W MobX tworzysz klasę JavaScript z wywołaniem makeObservable wewnątrz konstruktora, który jest twoim obserwowalnym magazynem (możesz użyć dekoratora @observable , jeśli masz odpowiedni loader). Następnie deklarujesz właściwości (stan) i metody ( akcje i wyliczone wartości ) klasy. Komponenty subskrybują ten obserwowalny magazyn, aby uzyskać dostęp do stanu, obliczonych wartości i działań.

Inną istotną cechą MobX jest zmienność. Pozwala na cichą aktualizację stanu w przypadku, gdy chcesz uniknąć skutków ubocznych.

Przykład interfejsu API MobX

Unikalną cechą MobX jest to, że tworzysz prawie czyste klasy ES6 z całą magią ukrytą pod maską. Wymaga mniej kodu specyficznego dla biblioteki, aby utrzymać koncentrację na logice.

CodeSandbox

 // stores/counter.js import { makeAutoObservable } from "mobx"; class CounterStore { value = 0; constructor() { makeAutoObservable(this); } increment() { this.value += 1; } decrement() { this.value -= 1; } } export default CounterStore; // index.js import React from "react"; import ReactDOM from "react-dom"; import { Provider } from "mobx-react"; import App from "./App"; import CounterStore from "./stores/counter"; ReactDOM.render( <Provider counter={new CounterStore()}> <App /> </Provider>, document.getElementById("root") ); // App.js import React from "react"; import { inject, observer } from "mobx-react"; const App = inject((stores) => ({ counter: stores.counter }))( observer(({ counter }) => { return ( <div> <div> <button onClick={() => counter.increment()}>Increment</button> <span>{counter.value}</span> <button onClick={() => counter.decrement()}>Decrement</button> </div> </div> ); }) ); export default App;

Atrybuty jakości

  • Użyteczność . Magazyn obserwowalny to pojedynczy punkt wejścia do zarządzania stanem. To sprawia, że ​​korzystanie z MobX jest proste, ponieważ masz jedyne miejsce na modyfikację.
  • Utrzymanie . To spory minus. Bez znajomości RxJS API nie będziesz w stanie osiągnąć pożądanego rezultatu. Używanie MobX w słabo wykwalifikowanym zespole może prowadzić do problemów z niespójnością stanu.
  • Wydajność . MobX składa się z niezależnych sklepów i umożliwia subskrybowanie tylko tych, których potrzebujesz. To bardzo skuteczne.
  • Testowalność . Sklepy obserwowalne to zwykłe obiekty JavaScript z ukrytą w środku reaktywną funkcjonalnością. Testowanie przebiega tak samo, jak w przypadku każdej innej klasy JavaScript.
  • Skalowalność . Obserwowalne magazyny są podzielone logicznie; nie ma trudności ze skalowaniem MobX.
  • Modyfikowalność . MobX umożliwia tworzenie niestandardowych obserwowalnych ze zmodyfikowanymi zachowaniami. Ponadto istnieje pojęcie zwane reakcjami. Reakcje modelują automatyczne skutki uboczne. Te rzeczy sprawiają, że MobX jest bardzo konfigurowalny.
  • Możliwość ponownego wykorzystania . MobX jest niezależny od frameworków, więc jest bardzo dobry w ponownym użyciu.
  • Ekosystem . Istnieją setki rozszerzeń dostępnych dla MobX.
  • Wspólnota . MobX ma wielu oddanych fanów. Istnieje ~ 1600 (~ 1000 odpowiedzi) pytań z tagiem mobx na Stack Overflow.
  • Puls . MobX jest regularnie aktualizowany i utrzymywany.

Odrzut

Recoil jest stosunkowo nowym graczem, najnowszym pomysłem zespołu React. Podstawową ideą jest prosta implementacja brakujących funkcji Reacta, takich jak stan współdzielony i dane pochodne.

Być może zastanawiasz się, dlaczego eksperymentalna biblioteka jest sprawdzana pod kątem projektów na poziomie przedsiębiorstwa. Przede wszystkim Recoil jest obecnie jednym z najczęściej dyskutowanych tematów w społeczności React. Po drugie, Recoil jest wspierany przez Facebooka i jest już używany w niektórych jego aplikacjach, co oznacza, że ​​w pewnym momencie stanie się stabilną wersją. Wreszcie, jest to całkowicie nowe podejście do udostępniania stanu w React i jestem pewien, że nawet jeśli Recoil jest przestarzały, pojawi się inne narzędzie, które podąża tą samą ścieżką.

Recoil opiera się na dwóch terminach: atom i selektor . Atom jest elementem współdzielonym. Komponent może subskrybować atom, aby uzyskać/ustawić jego wartość.

Diagram przedstawiający zarządzanie stanem za pomocą Recoil, pokazujący, w jaki sposób komponenty mogą subskrybować atom, aby pobrać lub ustawić jego wartość.

Jak widać na obrazku, tylko zasubskrybowane komponenty są ponownie renderowane po zmianie wartości. Dzięki temu Recoil jest bardzo wydajny.

Kolejną świetną rzeczą, którą Recoil ma po wyjęciu z pudełka, jest selektor . Selektor to wartość zagregowana z atomu lub innego selektora. Dla konsumentów nie ma różnicy między atomem a selektorem, wystarczy zasubskrybować jakąś reaktywną część i jej użyć.

Schemat ilustrujący użycie selektorów w Recoil, ich stosunek do atomów oraz zmiany spowodowane różnymi wartościami.

Za każdym razem, gdy zmieniany jest atom/selektor, selektory, które go używają (tzn. subskrybują go), są ponownie oceniane.

Przykład interfejsu API odrzutu

Kod Recoil znacznie różni się od jego konkurentów. Opiera się na hakach React i skupia się bardziej na strukturze stanu niż na mutowaniu tego stanu.

CodeSandbox

 // atoms/counter.js import { atom } from "recoil"; const counterAtom = atom({ key: "counter", default: 0 }); export default counterAtom; // index.js import React from "react"; import ReactDOM from "react-dom"; import { RecoilRoot } from "recoil"; import App from "./App"; ReactDOM.render( <RecoilRoot> <App /> </RecoilRoot>, document.getElementById("root") ); // App.js import React from "react"; import { useRecoilState } from "recoil"; import counterAtom from "./atoms/counter"; const App = () => { const [count, setCount] = useRecoilState(counterAtom); return ( <div> <div> <button onClick={() => setCount(count + 1)}>Increment</button> <span>{count}</span> <button onClick={() => setCount(count - 1)}>Decrement</button> </div> </div> ); }; export default App;

Atrybuty jakości

  • Użyteczność . Recoil jest jednym z najłatwiejszych w użyciu narzędzi, ponieważ działa jak useState w React.
  • Utrzymanie . Wszystko, co musisz zrobić w Recoil, to utrzymać selektory i haki wewnątrz komponentów – większa wartość, mniej szablonów.
  • Wydajność . Recoil buduje drzewo stanu poza Reactem. Drzewo stanu umożliwia uzyskanie i odsłuchanie tego, czego potrzebujesz, a nie zmian w całym drzewie. Jest również dobrze zoptymalizowany pod maską.
  • Testowalność . Recoil zapewnia mechanizm testowania swoich atomów i selektorów.
  • Skalowalność . Stan, który dzieli się na wiele niezależnych części, czyni go dobrym graczem pod względem skalowalności.
  • Modyfikowalność . Recoil odpowiada jedynie za przechowywanie wartości i ich agregacji. Nie ma przepływu danych, dzięki czemu można go łatwo dostosować.
  • Możliwość ponownego wykorzystania . Recoil opiera się na React. Nie można go ponownie wykorzystać w innym miejscu.
  • Ekosystem . W tej chwili nie ma ekosystemu dla Recoil.
  • Wspólnota . Recoil jest zbyt świeży, by mieć dużą społeczność. Jest około 70 pytań z tagiem recoiljs na Stack Overflow.
  • Puls . Odrzut jest aktualizowany rzadko (od dwóch ostatnich aktualizacji upłynęło sześć miesięcy). Ma również wiele otwartych problemów na GitHub.

Wybór odpowiedniego narzędzia do zarządzania stanem React

Jak te biblioteki globalnego zarządzania stanem React wypadają w przypadku aplikacji klasy korporacyjnej?

Recoil jest młody i świeży, ale w tej chwili nie ma społeczności ani ekosystemu. Mimo że Facebook nad tym pracuje, a API wydaje się obiecujące, ogromna aplikacja React nie może polegać na bibliotece ze słabym wsparciem społeczności. Ponadto jest eksperymentalny, co czyni go jeszcze bardziej niebezpiecznym. Zdecydowanie nie jest to dobra opcja dla aplikacji korporacyjnych React w dzisiejszych czasach, ale warto mieć ją na oku.

MobX i Redux nie podzielają żadnego z tych problemów i korzysta z nich większość dużych graczy na rynku. To, co odróżnia je od siebie, to krzywa uczenia się. MobX wymaga podstawowej znajomości programowania reaktywnego. Jeśli inżynierowie zaangażowani w projekt nie są wystarczająco wykwalifikowani, aplikacja może skończyć się niespójnością kodu, problemami z wydajnością i wydłużonym czasem rozwoju. MobX jest akceptowalny i spełni Twoje potrzeby, jeśli Twój zespół będzie świadomy reaktywności.

Redux ma również pewne problemy, głównie dotyczące skalowalności i wydajności. Jednak w przeciwieństwie do MobX istnieją sprawdzone rozwiązania tych problemów.

Biorąc pod uwagę wszystkie zalety i wady oraz biorąc pod uwagę moje osobiste doświadczenia, polecam Redux jako najlepszą opcję dla aplikacji React na poziomie korporacyjnym.