AdiPol Dev

Wydajne providery w React.js 馃敟

Miniaturka posta Wydajne providery w React.js 馃敟

Providery w Reakcie to聽zdecydowanie jedna z najistotniejszych rzeczy przy budowaniu aplikacji webowych. To w艂a艣nie z ich pomoc膮 raz na zawsze zako艅czyli艣my bol膮czk臋 w postaci przesy艂ania X razy props贸w w d贸艂 do naszych dzieci, 偶eby dzieli膰 stan mi臋dzy sob膮. Pomimo tego, 偶e context API znacznie u艂atwia nam zarz膮dzanie globalnym stanem, pomagaj膮c jednocze艣nie przy codziennej pracy, musimy by膰 czujni i uwa偶a膰 na pu艂apki, kt贸re mog膮 negatywnie wp艂yn膮膰 na wydajno艣膰 naszej strony. Dlatego w tym artykule chcia艂bym Ci臋 przestrzec przed najcz臋艣ciej pope艂nianymi b艂臋dami podczas u偶ywania provider贸w, kt贸re spotykam szczeg贸lnie w艣r贸d pocz膮tkuj膮cych programist贸w, zaczynaj膮cych dopiero swoj膮 przygod臋 z Reactem. Mam nadziej臋, 偶e po przeczytaniu tego posta lepiej zrozumiesz w jaki spos贸b dzia艂a share'owanie stanu w Reakcie, co pozwoli pisa膰 Ci bardziej 艣wiadomy oraz wydajny kod. Zapraszam do lektury 馃檪.

Jak dzieli膰 stan pomi臋dzy komponentami?

Proponuj臋 zacz膮膰 od kr贸tkiego przypomnienia, w jaki spos贸b tworzymy providery w Reakcie:

App.tsx
export const ThemeContext = createContext<'dark' | 'light'>('dark');

export const App = () => (
  <ThemeContext.Provider value={'dark'}>
    <Theme />
  </ThemeContext.Provider>
)
Theme.tsx
export const Theme = () => {
  const theme = useContext(ThemeContext);

  return <p>Current theme: <strong>{theme}</strong></p>;
}

Na pocz膮tku stworzyli艣my ThemeContext za pomoc膮聽funkcji createContext, zaimportowanej z Reacta. Do 艣rodka przekazali艣my domy艣ln膮 warto艣膰, kt贸ra zostanie u偶yta na wypadek, gdy React nie znajdzie 偶adnego providera w drzewie komponent贸w rodzic贸w. Nast臋pnie w g艂贸wnym komponencie App wyrenderowali艣my ThemeContext.Provider, kt贸ry dostarczy przekazan膮聽warto艣膰 value do swoich dzieci. Finalnie zosta艂o nam ju偶 tylko odczyta膰聽warto艣膰 z contextu za pomoc膮聽hooka useContext. No i to tyle, w艂a艣nie uda艂o nam si臋 przekaza膰 stan do swoich dzieci. Kod dzia艂a, a my mo偶emy rozej艣膰 si臋 do domu. Providery w Reakcie wydaj膮 si臋 na tyle proste i logiczne, 偶e przecie偶 nic z艂ego nie mo偶e si臋 tutaj wydarzy膰 - ale czy na pewno? P贸jd藕my o krok dalej i rozbudujmy troch臋 ten przyk艂ad.

Task z jiry

Zderzmy si臋 z rzeczywistym problemem, kt贸ry mo偶emy napotka膰 przy codziennej pracy z Reactem. Powiedzmy, 偶e dostali艣my taska na jirze, w kt贸rym to musimy zaimplementowa膰 dark theme oraz system logowania. W opisie zadania jasno postawiono nam wymogi, 偶e zar贸wno theme, jak i zalogowany user powinien by膰 艂atwo dost臋pny z poziomu ka偶dego komponentu. Do g艂owy od razu przychodzi nam context API. Zabieramy si臋 wi臋c do pracy.

Id膮c po linii najmniejszego oporu, pierwsza wersja naszego zadania mog艂aby wygl膮da膰 mniej wi臋cej w taki spos贸b:

Na pierwszy rzut oka wszystko dzia艂a, a my czujemy, 偶e mo偶emy zamkn膮膰 taska, wrzuci膰 brancha na code review i by膰 z siebie dumni, 偶e podo艂ali艣my kolejnemu ci臋偶kiemu zadaniu w robocie. Czy aby na pewno? Przyjrzyjmy si臋 temu bli偶ej.

Dzielony stan pomi臋dzy komponentami zdefiniowali艣my bezpo艣rednio w komponencie App, czyli g艂贸wnym komponencie naszej aplikacji. Jest to pierwszy, bardzo cz臋sty b艂膮d pope艂niany przez pocz膮tkuj膮cych programist贸w. Nie ma co si臋 dziwi膰, bo wydaje si臋 to dosy膰 logiczne i intucyjne, 偶eby dzielony stan wrzuci膰 na sam膮 g贸r臋 naszej aplikacji. Spowoduje to natomiast pewne problemy. Przede wszystkim, z perspektywy czytelno艣ci kodu, nie jest to najlepsze podej艣cie, bo w przysz艂o艣ci, gdy do naszej aplikacji dojd膮 jeszcze dwa globalne stany, plik App.tsx uro艣nie i nie b臋dzie da艂o si臋 go przeczyta膰. Ja natomiast chcia艂bym skupi膰 si臋 na drugej, troch臋 powa偶niejszej konsekwencji tego b艂臋du.

U偶ywaj膮c warto艣ci z globalnego stanu w komponencie oczekujemy od niej, 偶e gdy ta ulegnie zmianie, nasz komponent r贸wnie偶 si臋 przerenderuje wraz z najnowszymi danymi. Jak React sobie z tym radzi? Ka偶dy komponent, kt贸ry u偶ywa w sobie hooka useContext zostanie automatycznie przerenderowany, gdy zmieni si臋 warto艣膰 value pochodz膮ca z naszego providera. No w艂a艣nie - powinny przerenderowa膰 si臋 tylko komponenty u偶ywaj膮ce hooka useContext, czy tak si臋 u nas dzieje?

Podgl膮d 1

Zauwa偶, 偶e zmieniaj膮c warto艣膰聽stanu theme, pochodz膮cego z ThemeContext.Provider, przerenderowywuj膮 si臋 wszystkie komponenty, a nie tylko te, kt贸re wykorzystuj膮 hooka useContext. Dlaczego tak si臋 dzieje? Zerknijmy na plik App.tsx:

App.tsx
export const App = () => {
  const [theme, setTheme] = useState<Theme>("dark");
  const [user, setUser] = useState<User>({
    id: 1,
    email: "foo@gmail.com",
  });

  const changeTheme = (theme: Theme) => setTheme(theme);
  const changeUser = (user: User) => setUser(user);

  return (
    <ThemeContext.Provider value={{ theme, changeTheme }}>
      <UserContext.Provider value={{ user, changeUser }}>
        <Header />
        <main style={{ padding: "20px" }}>
          <User />
          <Theme />
        </main>
        <Footer />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
};

Klikaj膮c przycisk Toggle theme, wywo艂ujemy funkcj臋 changeTheme. Funkcja ta wywo艂uje pod spodem setTheme, aby zaktualizowa膰 nasz stan. Tylko musimy pami臋ta膰, 偶e wywo艂anie settera z useState spowoduje ponowne wykonanie funkcji komponentu, 偶eby ten m贸g艂 otrzyma膰 najnowsze dane.

Dlatego w艂a艣nie po wywo艂aniu funkcji changeTheme, wywo艂ujemy setTheme, co z kolei wywo艂uje funkcj臋 App, a poniewa偶 w komponencie App wyrenderowali艣my komponenty Header, User, Theme oraz Footer - wszystkie zostan膮聽wyrenderowane od nowa (nie bior膮c pod uwag臋 memoizacji). Musimy wi臋c pami臋ta膰, 偶eby nie zagnie偶d偶a膰 komponent贸w naszej aplikacji bezpo艣rednio w providerach, bo doprowadzi to do przerenderowania ca艂ej aplikacji od nowa po zmianie jakiegokolwiek stanu, i przy okazji kompletnie nie korzystamy z optymalizacji, o kt贸rej wcze艣niej wspomnia艂em.

Mo偶emy poradzi膰 sobie z tym problemem za pomoc膮 propsa children. Zabierzmy si臋 zatem za refaktoryzacj臋.

Przeniesienie provider贸w do osobnego pliku

Oczy艣ci艂em troch臋 plik App.tsx przenosz膮c logik臋 odpowiedzialn膮 za globalny stan do osobnego pliku - AppProviders.tsx. Zobaczmy jak to si臋 teraz prezentuje:

Patrz膮c na nasz projekt na pewno 艣mia艂o mo偶emy powiedzie膰, 偶e sta艂 si臋 on du偶o czytelniejszy. Przenosz膮c kod do pliku AppProviders.tsx nie za艣miecamy ju偶 niepotrzebnie komponentu App. Wci膮偶 nie jest idealnie, ale zawsze jest to jaki艣 krok do przodu 馃檪. Skontrolujmy rerendery naszych komponent贸w:

Podgl膮d 2

Jest troch臋 lepiej, prawda? Przestali艣my ju偶 niepotrzebnie rerenderowa膰聽komponenty, kt贸re nie korzystaj膮 z 偶adnego kontekstu i tak naprawd臋 nie maj膮 nawet powodu, 偶eby si臋 ponownie wyrenderowa膰. Natomiast zauwa偶y艂em teraz troch臋 inny problem. Po zmianie warto艣ci stanu theme, React przerenderowywuje wszystkie komponenty, kt贸re korzystaj膮 z hooka useContext. Dlaczego tak si臋 dzieje?

Tak jak ju偶 wcze艣niej wspomnia艂em, React automatycznie przerenderuje komponent korzystaj膮cy z hooka useContext, gdy zmieni si臋 warto艣膰 value w providerze, po to, aby zaktualizowa膰 go o najnowsze dane. No dobra, ale sk膮d React ma wiedzie膰, 偶e warto艣膰 uleg艂a zmianie i trzeba ponownie wyrenderowa膰 nasz komponent? Wszystko dzieje si臋 za spraw膮 Object.is. React u偶ywa tej metody, aby por贸wna膰 warto艣膰 value wzgl臋dem poprzedniego i aktualnego rendera, a nast臋pnie na podstawie zwr贸conego wyniku zadecyduje, czy warto艣膰 uleg艂a zmianie i trzeba przerenderowa膰 komponent, czy mo偶e jednak zostawiamy go w spokoju.

Posiadaj膮c t臋聽wiedz臋, skupmy si臋 na komponencie AppProviders:

AppProviders.tsx
export const AppProviders = ({
  children,
}: {
  readonly children: ReactNode;
}) => {
  const [theme, setTheme] = useState<Theme>("dark");
  const [user, setUser] = useState<User>({
    id: 1,
    email: "foo@gmail.com",
  });

  const changeTheme = (theme: Theme) => setTheme(theme);
  const changeUser = (user: User) => setUser(user);

  return (
    <ThemeContext.Provider value={{ theme, changeTheme }}>
      <UserContext.Provider value={{ user, changeUser }}>
        {children}
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
};

Znowu wywo艂uj膮c funkcj臋 changeTheme, ta powoduje wywo艂anie setTheme, co z kolei przek艂ada si臋 na ponowne wywo艂anie funkcji AppProviders. Dlatego toggle'uj膮c stan za pomoc膮 buttona na stronie, spowoduje to rerender komponentu AppProviders. No spoko, ale jak to si臋 ma do sposobu por贸wnywania warto艣ci value, o kt贸rym wcze艣niej wspomnia艂em?

Spojrzmy na t臋 linijk臋: <UserContext.Provider value={{ user, changeUser }}>. Przekazujemy w niej do propsa value obiekt z naszym userem wraz z funkcj膮 do jego zmiany. Zauwa偶, 偶e w 偶aden spos贸b nie zapisujemy tego obiektu. Jest on tworzony od nowa za ka偶dym razem przy wywo艂aniu naszej funkcji, co oznacza, 偶e co render b臋dzie posiada艂 on inn膮聽referencj臋. Zagl膮daj膮c do dokumentacji Object.is mo偶emy przeczyta膰, 偶e obiekty por贸wnywane s膮聽za pomoc膮聽referencji, ju偶 rozumiesz?

Pomimo tego, 偶e nie zmienili艣my 偶adnej warto艣ci stanu providera UserContext.Provider, przez to, 偶e zmieni艂a si臋 referencja do obiektu w propsie value, React my艣li, 偶e warto艣膰 w tym providerze r贸wnie偶 uleg艂a zmianie i trzeba przerenderowa膰聽wszystkie komponenty, kt贸re u偶ywaj膮 kontekstu UserContext. Tak w艂a艣nie dzia艂a pod spodem od艣wie偶anie komponent贸w korzystaj膮cych z kontekst贸w.

To jest drugi pow贸d dla kt贸rego umieszczanie wszystkich provider贸w w jednym komponencie r贸wnie偶 nie jest najlepsz膮 opcj膮. No dobra, ale czy mo偶emy co艣 z tym zrobi膰? Spr贸bujmy 馃檪.

Dodanie memoizacji

Nasz komponent AppProviders ponownie uleg艂 zmianie. Zaraz przyjrzymy mu si臋 bli偶ej, ale najpierw skontrolujmy rerendery:

Podgl膮d 3

Co widzimy? Od teraz React przerenderowywuje tylko i wy艂膮cznie komponenty, w kt贸rych globalny stan faktycznie uleg艂 zmianie. Jest to zdecydowanie najbardziej optymalne rozwi膮zanie, bo od艣wie偶amy tylko to, co rzeczywi艣cie tego potrzebuje. No dobra, zajrzyjmy do 艣rodka i przeanalizujmy zmiany:

AppProviders.tsx
export const AppProviders = ({
  children,
}: {
  readonly children: ReactNode;
}) => {
  const [theme, setTheme] = useState<Theme>("dark");
  const [user, setUser] = useState<User>({
    id: 1,
    email: "foo@gmail.com",
  });

  const changeTheme = useCallback((theme: Theme) => setTheme(theme), []);
  const changeUser = useCallback((user: User) => setUser(user), []);

  const themeValue = useMemo(
    () => ({ theme, changeTheme }),
    [theme, changeTheme],
  );

  const userValue = useMemo(() => ({ user, changeUser }), [user, changeUser]);

  return (
    <ThemeContext.Provider value={themeValue}>
      <UserContext.Provider value={userValue}>{children}</UserContext.Provider>
    </ThemeContext.Provider>
  );
};

Zauwa偶y艂e艣/a艣 co si臋 zmieni艂o w nowym komponencie AppProviders? Jak wida膰, do naszych funkcji oraz warto艣ci provider贸w dosz艂y dwa hooki - useCallback oraz useMemo. Jaki to ma cel?

Od teraz React b臋dzie cache'owa艂 definicje naszych funkcji oraz obiekt贸w i dop贸ki nie zmieni si臋聽tablica zale偶no艣ci, co ka偶dy render otrzymamy dok艂adnie t臋 sam膮 referencj臋, co wywo艂anie wcze艣niej. Jaki to mia艂o wp艂yw na useContext?

Tym razem zmieniaj膮c warto艣膰 stanu theme za pomoc膮 funkcji changeTheme, przerenderowanie komponentu spowoduje zmian臋 referencji tylko i wy艂膮cznie obiektu themeValue, co za tym idzie - przerenderuj膮 si臋 tylko i wy艂膮cznie komponenty korzystaj膮ce z jego kontekstu. Dlaczego?

Do tablicy zale偶no艣ci themeValue przekazali艣my dwa obiekty: theme oraz changeTheme. Callback changeTheme nie powinien nigdy zmieni膰聽swojej referencji, poniewa偶 posiada pust膮 tablic臋 zale偶no艣ci. Natomiast wywo艂uj膮c funkcj臋 setTheme zmutowali艣my stan, co za tym idzie - zmienili艣my warto艣膰 theme, a wi臋c useMemo w themeValue zwr贸ci now膮 referencj臋. Poniewa偶 referencja obiektu themeValue uleg艂a zmianie, Object.is zwr贸ci false, co za tym idzie - React musi od艣wie偶y膰 komponenty korzystaj膮ce z kontekstu ThemeContext.

W przypadku obiektu userValue, podczas zmiany stanu theme, nie zmieni艂a si臋 偶adna warto艣膰 w tablicy zale偶no艣ci, dlatego dostajemy za ka偶dym razem t臋 sam膮 referencj臋, a React nie ma powodu, aby przerenderowa膰 komponenty u偶ywaj膮ce kontekstu UserContext. W ten spos贸b udowodni艂em, 偶e por贸wnywanie warto艣ci value faktycznie odbywa si臋 za pomoc膮 metody Object.is.

Fajnie, wszystko dzia艂a jak nale偶y, ale czy mo偶emy zrobi膰 to lepiej? Oczywi艣cie - na tworzenie provider贸w znam jeszcze lepszy spos贸b, poka偶臋 Ci go 馃檪.

Wydzielenie ka偶dego providera do osobnego pliku

Zamiast trzyma膰 stan wszystkich provider贸w w jednym komponencie, mo偶emy stworzy膰 komponent ze stanem dla ka偶dego providera osobno:

Wprowadzili艣my kilka poprawek, ale zanim si臋 im przyjrzymy, ponownie zacznijmy od skontrolowania sytuacji rerender贸w:

Podgl膮d 4

Tak jak widzimy, nasz kod dalej zachowuje si臋 w najbardziej optymalny spos贸b, od艣wie偶aj膮c tylko to, co potrzebne. Zajrzyjmy wi臋c do 艣rodka i om贸wmy zmiany, kt贸re zasz艂y:

AppProviders.tsx
export const AppProviders = ({
  children,
}: {
  readonly children: ReactNode;
}) => (
  <ThemeProvider>
    <UserProvider>{children}</UserProvider>
  </ThemeProvider>
);
ThemeProvider.tsx
export const ThemeProvider = ({
  children,
}: {
  readonly children: ReactNode;
}) => {
  const [theme, setTheme] = useState<Theme>("dark");

  const changeTheme = (theme: Theme) => setTheme(theme);

  return (
    <ThemeContext.Provider value={{ theme, changeTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};
UserProvider.tsx
export const UserProvider = ({
  children,
}: {
  readonly children: ReactNode;
}) => {
  const [user, setUser] = useState<User>({
    id: 1,
    email: "foo@gmail.com",
  });

  const changeUser = (user: User) => setUser(user);

  return (
    <UserContext.Provider value={{ user, changeUser }}>
      {children}
    </UserContext.Provider>
  );
};

Z komponentu AppProviders pozbyli艣my si臋 tony kodu odpowiedzalnego za zarz膮dzanie stanem dw贸ch provider贸w w naszej aplikacji. Zosta艂 on przeniesiony do osobnych komponent贸w, utworzonych specjalnie pod ka偶dy provider - ThemeProvider oraz UserProvider. Jakie mamy zalety? Przede wszystkim pozbyli艣my si臋聽problemu z czytelno艣ci膮, o kt贸rym wspomnia艂em na samym pocz膮tku tego artyku艂u. Od teraz chc膮c edytowa膰 dany provider, nie przebijamy si臋 przez mas臋 logiki pochodz膮cej z innego providera. Zamiast tego udajemy si臋 do osobnego komponentu, gdzie wszystko jest 艂adnie uporz膮dkowane. Jednocze艣nie jeste艣my przygotowani na ewentualno艣膰 dodania kolejnych provider贸w w przysz艂o艣ci, bez obawy o czytelno艣膰 kodu.

Bardziej spostrzegawcze osoby zauwa偶y艂y pewnie, 偶e przestali艣my r贸wnie偶 u偶ywa膰 useCallback oraz useMemo, a mimo wszystko kod zachowuje si臋 tak, jak tego oczekiwali艣my. Dlaczego tak si臋 dzieje?

Zauwa偶, 偶e od teraz nie renderujemy 偶adnego komponentu bezpo艣rednio w providerach. Zamiast tego przekazujemy je w propsie children. Oznacza to, 偶e renderuj膮c ponownie komponent ThemeProvider, nie spowoduje on przerenderowania komponentu UserProvider, poniewa偶 go tam nie ma. Zwracamy tylko obiekt children, kt贸ry zosta艂 utworzony wy偶ej. Tak wi臋c zmieniaj膮c stan w komponencie ThemeProvider, nie spowoduje on rerenderu UserProvider, co za tym idzie - referencja value nie ulegnie zmianie podczas mutacji tego stanu, dzi臋ki czemu React nie przerenderuje komponent贸w korzystaj膮cych z contextu UserContext.

Zdecydowanie jest to najlepsze podej艣cie z wy偶ej wymienionych. Dzi臋ki niemu nie tylko zyskali艣my na czytelno艣ci kodu, ale i pozbyli艣my si臋聽hook贸w useCallback oraz useMemo, co na pewno wp艂ynie pozytywnie na optymalizacj臋 naszej strony Internetowej.

Inny spos贸b na rozwi膮zanie zadania

Zabieraj膮c si臋 za taska z jiry, tak naprawd臋 mo偶emy rozwi膮za膰 go na jeszcze jeden spos贸b. Celowo pomin膮艂em to podej艣cie, 偶eby nie wprowadza膰 zamieszania, dlatego sp贸jrzmy teraz na przyk艂ad:

Osoby pocz膮tkuj膮ce mog膮 nie wpa艣膰 na pomys艂, 偶eby stworzy膰 dwa osobne providery - zamiast tego utworz膮 jeden wielki provider, przechowuj膮cy globalny stan naszej ca艂ej aplikacji. Jest to powa偶ny b艂膮d, kt贸ry zaraz Ci wyt艂umacz臋, ale najpierw skontrolujmy rerendery na naszej stronie:

Podgl膮d 5

Ponownie wr贸cili艣my do problemu, kt贸ry przez ca艂y czas pr贸bowali艣my zwalczy膰 - rerenderujemy wszystkie komponenty, u偶ywaj膮ce hooka useContext. Musz臋 Ci臋 niestety zmartwi膰 - tutaj nie da si臋 tego naprawi膰 i tym razem nawet memoizacja nam nie pomo偶e - dlaczego? Sp贸jrzmy na plik AppProvider.tsx:

AppProvider.tsx
export const AppProvider = ({ children }: { readonly children: ReactNode }) => {
  const [theme, setTheme] = useState<Theme>("dark");
  const [user, setUser] = useState<User>({
    id: 1,
    email: "foo@gmail.com",
  });

  const changeTheme = (theme: Theme) => setTheme(theme);
  const changeUser = (user: User) => setUser(user);

  return (
    <AppContext.Provider value={{ theme, user, changeTheme, changeUser }}>
      {children}
    </AppContext.Provider>
  );
};

Widzimy, 偶e komponent AppProvider odpowiedzialny jest za przechowywanie globalnego stanu ca艂ej naszej aplikacji. Jaki jest w tym problem?

Tak jak wcze艣niej wspomnia艂em, React analizuje kiedy ma przerenderowa膰 komponenet za pomoc膮聽metody Object.is. W tym przypadku wywo艂anie funkcji changeTheme spowoduje wywo艂anie setTheme, co z kolei prze艂o偶y si臋 na wywo艂anie funkcji AppProvider, czyli wykonamy rerender. Przy nast臋pnym wywo艂aniu funkcji ponownie tworzymy obiekt { theme, user, changeTheme, changeUser }, wi臋c dostaje on now膮 referencj臋, Object.is zwraca false i w konsekwencji dostajemy rerender. No dobra, to mo偶e znowu spr贸bujemy ugasi膰 po偶ar za pomoc膮聽memoizacji?

AppProvider.tsx
export const AppProvider = ({ children }: { readonly children: ReactNode }) => {
  const [theme, setTheme] = useState<Theme>("dark");
  const [user, setUser] = useState<User>({
    id: 1,
    email: "foo@gmail.com",
  });

  const changeTheme = useCallback((theme: Theme) => setTheme(theme), []);
  const changeUser = useCallback((user: User) => setUser(user), []);

  const value = useMemo(
    () => ({ theme, user, changeTheme, changeUser }),
    [theme, user, changeTheme, changeUser],
  );

  return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
};

Widzimy, 偶e memoizacja w tym przypadku nie ma kompletnie 偶adnego sensu, bo do tablicy zale偶no艣ci przekazujemy theme, kt贸ry zmieni si臋 po wywo艂aniu funkcji changeTheme, tak wi臋c obiekt value i tak za ka偶dym razem dostanie now膮 referencj臋.

Upraszczaj膮c to co napisa艂em wy偶ej - zamiast tworzy膰 jeden ogromny provider, lepiej utworzy膰聽kilka mniejszych, przechowuj膮cych ma艂e, zgrupowane warto艣ci. Dzi臋ki temu unikniemy sytuacji rerenderowania komponent贸w, kt贸re co prawda korzystaj膮 z tego samego kontekstu, ale warto艣膰, kt贸rej u偶ywamy si臋 nie zmieni艂a, wi臋c ten rerender kompletnie nic nie wnosi i wp艂yna negatywnie na wydajno艣膰.

Problem rerenderowania komponent贸w, kt贸rych u偶ywana warto艣膰 ze stanu nie uleg艂a zmianie to dosy膰 powszechny temat, dlatego powsta艂o na to kilka rozwi膮za艅. Zamiast korzysta膰 z domy艣lnego sposobu na globalny stan w Reakcie, mo偶emy pos艂u偶y膰 si臋聽popularnymi bibliotekami do state managment'u. Pomocny mo偶e okaza膰 si臋 tutaj chocia偶by zustand, kt贸ry trzyma sw贸j stan poza Reactem i synchronizuje go za pomoc膮 hooka useSyncExternalStore. Rozwi膮za艂 on m.in ten, jak i mas臋 innych problem贸w, dlatego zainteresowanych odsy艂am do dokumentacji 馃檪.

Podsumowanie

To tyle na dzisiaj. Tym artyku艂em chcia艂em rozja艣ni膰 Ci troch臋, w jaki spos贸b dzia艂a globalny stan w Reakcie, 偶eby艣 m贸g艂 pisa膰 bardziej 艣wiadomy oraz wydajny kod. Mam nadziej臋, 偶e wynios艂e艣 z niego jak膮艣 cenn膮 lekcj臋, kt贸r膮 zastosujesz przy codziennej pracy. Zainteresowanych zach臋cam r贸wnie偶 do podzielenia si臋 swoimi przemy艣leniami w sekcji komentarzy 馃檪.

馃憠馃徎 Zauwa偶y艂e艣/a艣 b艂膮d? 馃憟馃徎

Edytuj ten post na GitHubie!