Why Everyone Loves Zustand

Theo - t3․gg
15 Sept 202429:27

Summary

TLDRLe script parle de l'utilisation de la bibliothèque Zustand pour la gestion d'état dans React. L'auteur clarifie la prononciation de 'Zustand' et discute de son utilisation pour refactoriser une application appelée Teemo. Il compare Zustand à Redux et React Context, soulignant son API simple et léger. Le script explore également les défis de l'utilisation de TypeScript avec Zustand et la manière de gérer les effets de bord avec cette bibliothèque. Enfin, il mentionne les avantages de Zustand pour la gestion des états asynchrones et évoque d'autres méthodes de gestion d'état à venir.

Takeaways

  • 🗣️ L'auteur discute de la prononciation de 'zustand', une bibliothèque de gestion d'état React, et résout ce point polémique avec un sondage sur Reddit.
  • 📱 Il présente un aperçu de l'application Teemo, qui est utilisée pour tester et comparer différentes solutions de gestion d'état, y compris Zustand.
  • 🔧 L'auteur refactorise l'application Teemo pour utiliser Zustand, passant en revue les étapes de transformation et les avantages de cette approche.
  • 📦 'Zustand' est décrit comme une bibliothèque de gestion d'état légère qui fournit un hook React pour gérer l'état, se basant sur le modèle Flux, tout en le simplifiant davantage par rapport à Redux.
  • ⚙️ L'auteur explique en détail comment configurer un magasin (store) simple avec Zustand, y compris la mise en place d'une valeur et d'une action pour la modifier.
  • 🔄 Il mentionne les défis de TypeScript avec Zustand et suggère des solutions pour gérer les types d'état et les actions.
  • 📈 L'auteur compare la taille de l'ensemble des packages npm de Zustand, Redux et React, soulignant l'efficacité et la simplicité de Zustand.
  • 🔄 Il discute de la refactorisation des composants communs dans Teemo, abordant les difficultés de garde des composants purs lors de la transition de React Context à Zustand.
  • 🛠️ L'auteur explore la gestion des effets secondaires avec Zustand, en particulier comment exécuter des actions lors du démarrage de l'application sans avoir recours aux hooks `useEffect`.
  • 🔗 Il mentionne l'intérêt de la création conditionnelle de magasins (stores) dans Zustand en fonction de conditions préalables, ce qui évite la complexité des états globaux.
  • 🌐 L'auteur conclut en soulignant les avantages de Zustand pour la gestion d'état dans les applications React, notamment la simplicité d'utilisation, la facilité de personnalisation et la réduction du nombre de rerendus non désirés.

Q & A

  • Pourquoi l'auteur a-t-il décidé de parler de zustand dans cette vidéo?

    -L'auteur a remarqué un sondage sur le subreddit React où zustand semblait être populaire parmi les développeurs React, ce qui l'a motivé à en faire une vidéo.

  • Quel est le problème de prononciation avec 'zustand' que l'auteur mentionne?

    -L'auteur a constaté qu'il prononçait incorrectement le nom de la bibliothèque 'zustand', ce qui a provoqué des réactions chez ses collègues et il a donc cherché la prononciation correcte sur Google.

  • Quelle est la taille du package npm de zustand une fois minifié?

    -Le package npm de zustand a une taille de 3,1 kilobytes minifiée.

  • Comment l'auteur a-t-il refactorisé l'application Teemo pour utiliser zustand?

    -L'auteur a réfactorisé l'application Teemo en utilisant zustand pour la gestion d'état, en changeant principalement la gestion de l'authentification utilisateur et le stockage du profil.

  • Quel est le modèle de conception de zustand?

    -Zustand est basé sur le modèle Flux, similaire à Redux, mais avec une simplification supplémentaire pour rendre la gestion d'état plus facile.

  • Pourquoi l'auteur préfère-t-il zustand sur les autres solutions de gestion d'état?

    -L'auteur apprécie la simplicité d'API de zustand, sa taille légère et la facilité avec laquelle il peut être intégré et utilisé dans des applications React.

  • Quels sont les défis que l'auteur a rencontrés lors de la refactorisation de Teemo avec zustand?

    -L'un des principaux défis a été la refactorisation des composants du package commun qui utilisaient React Context, car ils ne fonctionnaient pas avec la version de zustand de Teemo.

  • Comment l'auteur a-t-il géré les effets de bord dans zustand?

    -L'auteur a mentionné qu'il a d'abord été perplexe par la gestion des effets de bord, mais a ensuite trouvé une solution en utilisant une action zustand pour faire une requête API et en la déclenchant immédiatement après la déclaration du magasin.

  • Quelle est la différence majeure entre zustand et Redux Toolkit en termes de performance?

    -Zustand est significativement plus léger que Redux Toolkit, avec un package minifié 44 fois plus petit, ce qui en fait une option attrayante pour les applications où la taille du bundle est une préoccupation.

  • Pourquoi l'auteur recommande-t-il l'utilisation de sélecteurs avec zustand?

    -L'auteur recommande les sélecteurs pour optimiser les performances en minimisant les rendus des composants, en ne déclenchant des mises à jour que pour les parties de l'état qui sont réellement modifiées.

Outlines

00:00

😀 Introduction to Zustand and React State Management

Le présentateur aborde la prononciation de 'Zustand', une bibliothèque de gestion d'état pour React, qu'il préfère. Il mentionne un sondage sur le subreddit de React qui montre l'popularité de Zustand parmi les développeurs. Il explique qu'ils enregistrent souvent des vidéos en direct et que ses spectateurs sont mécontents car il prononce incorrectement le nom de la bibliothèque. Il partage son expérience de refactorisation de l'application 'Teemo' pour utiliser Zustand, et discute de la simplicité d'utilisation de Zustand par rapport à Redux et React Query.

05:02

🔧 Refactoring Teemo to Use Zustand

Le présentateur détaille le processus de refactorisation de l'application 'Teemo' pour utiliser Zustand au lieu de React Context. Il explique les avantages de l'utilisation de hooks dans Zustand, la simplicité de la configuration initiale et la facilité d'accès aux valeurs d'état et aux actions. Il compare la taille de l'ensemble du package Zustand avec celles de React et Redux, soulignant l'efficacité de Zustand en termes de performance et de taille de package.

10:03

🤔 Handling Side Effects and Middleware in Zustand

Le présentateur discute de la gestion des effets secondaires dans Zustand, en particulier comment exécuter des actions au démarrage de l'application. Il partage sa solution pour contourner l'absence de documentation sur la création de middleware personnalisée. Il parle également de son expérience avec le pattern de conception Flux et comment Zustand simplifie cela davantage par rapport à Redux.

15:04

🎮 Building a Game Engine with Zustand

Le présentateur montre comment il a construit un moteur de jeu entièrement avec Zustand lors de la 'Dogecoin Rush'. Il explique comment gérer les mises à jour fréquentes de l'état et la nécessité d'être prudent avec les mises à jour d'état pour éviter les redessins constants. Il aborde également l'utilisation des sélecteurs pour optimiser les rendus des composants React.

20:05

📊 Performance Optimization with Zustand Selectors

Le présentateur explore l'utilisation des sélecteurs dans Zustand pour optimiser les performances. Il explique comment les sélecteurs permettent de minimiser les redessins inutiles en ne mettant à jour que les parties pertinentes de l'application. Il compare cette approche avec les limitations de React Context et la nécessité d'utiliser des mécanismes de memorisation pour éviter les redessins non désirés.

25:09

👍 Conclusion on Zustand and Future State Management Patterns

Le présentateur conclut en soulignant les avantages de Zustand comme solution de gestion d'état légère et bien conçue, particulièrement pour ceux familiers avec le pattern Flux et Redux. Il mentionne également d'autres patterns de gestion d'état à venir, tels que les patterns atomiques de Facebook et la bibliothèque Jotai qui remplace Recoil. Il encourage les spectateurs à explorer davantage les solutions proposées par le collectif Point 97, qui a contribué à de nombreux projets innovants dans le domaine du développement web.

Mindmap

Keywords

💡Zustand

Zustand est une bibliothèque de gestion d'état légère dans le monde React. Elle permet de gérer l'état de manière centralisée sans avoir à envelopper l'application entière avec un fournisseur de contexte. Dans la vidéo, l'auteur discute de l'efficacité de Zustand en le comparant à d'autres méthodes de gestion d'état et montre comment il peut réduire considérablement le code nécessaire pour implémenter des fonctionnalités spécifiques.

💡Gestion d'état

La gestion d'état fait référence à la manière dont les applications maintiennent et modifient leurs données au fil du temps. Dans le script, la gestion d'état est abordée en détail, en utilisant Zustand pour simplifier la gestion des états partagés entre plusieurs composants React.

💡React

React est une bibliothèque JavaScript populaire pour construire des interfaces utilisateur. Le script mentionne React comme infrastructure dans laquelle Zustand est utilisé pour améliorer la gestion d'état des applications.

💡Flux

Flux est un modèle d'architecture d'application conçu pour gérer les données dans les applications JavaScript. Il est mentionné dans le script comme l'un des modèles de conception sur lesquels Zustand est basé, tout comme Redux, mais avec une approche encore plus simplifiée.

💡Redux

Redux est une bibliothèque open-source pour JavaScript qui fournit un conteneur d'état prévisible. Dans le script, Redux est comparé à Zustand pour illustrer la simplicité et la légèreté de Zustand en tant que solution de gestion d'état.

💡Hooks

Les hooks sont une fonctionnalité introduite dans React 16.8 qui permet d'utiliser des états et autres fonctionnalités de React sans écrire de classe. Le script explique comment les hooks sont utilisés avec Zustand pour accéder et mettre à jour l'état de l'application.

💡Contexte

Le contexte est un composant React qui permet de passer des données à travers les niveaux de l'arborescence des composants sans avoir à les passer manuellement à chaque niveau. Dans le script, l'auteur discute des avantages de Zustand par rapport au contexte React pour la gestion d'état.

💡Mise à jour asynchrone

Les mises à jour asynchrones sont des modifications d'état qui se produisent en dehors du cycle de rendu de React. Le script aborde comment Zustand gère les opérations asynchrones, ce qui permet d'éviter l'utilisation de chaînes de promesses et de simplifier la logique de mise à jour de l'état.

💡Selectors

Les selectors sont des fonctions qui calculent des valeurs dérivées à partir de l'état global. Dans le script, l'auteur montre comment les selectors sont utilisés avec Zustand pour optimiser les performances des composants React en les rendant uniquement lorsque les valeurs sélectionnées changent.

💡TypeScript

TypeScript est un super-ensemble de JavaScript qui ajoute des types et d'autres fonctionnalités. Le script mentionne TypeScript dans le contexte des défis rencontrés lors de l'utilisation de Zustand avec ce langage, soulignant l'importance de la typage pour la maintenance et la scalabilité des applications.

Highlights

Zustand is a favorite library for state management in React due to its simplicity and efficiency.

Pronunciation of 'Zustand' is humorously debated and clarified in the video.

Teemo, a sample app, is used to demonstrate the practical application of Zustand.

Zustand is compared to Redux, showing its simplification of the Flux pattern.

Zustand's setup for a simple store with a count value and increment action is showcased.

The lightweight nature of Zustand is highlighted, with its minified size of 3.1 kilobytes.

Refactoring Teemo to use Zustand from React Context is discussed, emphasizing ease of use.

The challenge of handling side effects in Zustand and a creative solution is presented.

Zustand's generated hooks are praised for their simplicity and ability to prevent unnecessary renders.

The use of selectors in Zustand to optimize component updates is explained.

Zustand's approach to async operations is discussed, highlighting its straightforward nature.

The benefits of switching from React Context to Zustand for state management are outlined.

A real-world example of Zustand's application in a Dogecoin simulator app is shared.

The video provides a direct comparison between vanilla React, React with React Query, and Zustand.

Zustand's performance in updating only specific parts of the app is demonstrated.

The video concludes with a positive endorsement of Zustand and a look forward to exploring other state management patterns.

Transcripts

play00:00

zustand my favorite library to

play00:01

mispronounce everyone else's favorite

play00:03

library to use for State Management in

play00:05

react before we go into why we're

play00:07

talking about it today I need to settle

play00:09

this pronunciation thing once and for

play00:11

all because my chat they're mad at me

play00:13

right now if you didn't already know we

play00:14

film a lot of videos live and at this

play00:16

moment they're upset with me I started

play00:18

Googling I found this very fun poll on

play00:20

the react subreddit where it seems like

play00:22

my zustand has successfully pierced the

play00:25

brains of react devs but we need to hear

play00:28

it we need a proper hear the Google

play00:31

pronunciation so for the first time ever

play00:34

y'all get to hear the right way to

play00:35

pronounce zustand on my

play00:37

channel and that is the last time you

play00:39

will ever hear zustan pronounced

play00:41

correctly on my channel because I am

play00:42

never ever saying it that way I don't

play00:44

care with that all settled let's talk

play00:47

about why Zan's a community favorite as

play00:49

soon as I saw this blog post I knew it

play00:50

had to be a video so let's do it by the

play00:53

last monthly post our sample app Teemo

play00:56

was finally ready for some experiments

play00:57

so in this post let's look at zustan to

play00:59

see why has become a favorite in the

play01:01

community to do this I'm going to

play01:02

refactor Teemo to use zustand so that we

play01:04

can see what it's like in practice what

play01:05

is Teemo how much code can that query

play01:08

save oh boy uh yeah I'm not going to

play01:10

read this one live but if you are

play01:12

interested I'll leave the link in the

play01:14

description these posts actually seem

play01:16

very useful and super super practical

play01:18

but I want to figure out what Teo is it

play01:20

has three screens one for logging in and

play01:22

signing in another for viewing and

play01:23

changing time entries and a screen with

play01:25

a timer for creating new time entries

play01:27

now we know what the app is now you guys

play01:28

know there's a great blog post about

play01:29

about tan stack query with it react

play01:31

query I'm sure that one is nuts that

play01:33

it's just showing how much you can

play01:34

delete I had a great convo yesterday

play01:36

with an employee at a popular AI company

play01:38

about how they could use react query to

play01:40

build a way simpler front-end experience

play01:42

and it was mind-blowing for both of us

play01:44

in the process so once again react query

play01:46

is the search Theo react query and

play01:48

you'll find a bunch of my YouTube videos

play01:50

explaining why we're here for zustand

play01:52

sorry zand second and only time you'll

play01:54

ever hear me pronounce it even close to

play01:55

correct so what is zand it's a small

play01:58

State Management library that provides a

play01:59

react HS to manage the state it is based

play02:01

on the flux pattern like Redux but it's

play02:03

similar to how Redux simplified the

play02:05

original flux pattern zustan simplifies

play02:07

it even further for example here's how

play02:09

you would set up a simple store with a

play02:10

count value and an action to increase it

play02:13

import create from zustand use count

play02:15

store create we have the set function

play02:16

here we return an object it has a

play02:19

property count which is starting at zero

play02:21

and then it has increment which is a

play02:22

function that you can call and we give

play02:25

this an inline function definition set

play02:28

and then here set can take a function as

play02:30

well and that function gets called with

play02:33

the current state then we can use that

play02:35

in the response here so it's similar to

play02:36

when you use something like UST State

play02:39

the state updator function can either be

play02:41

called with a bare value or can be

play02:42

called with a function that it will call

play02:44

with the current value super simple API

play02:46

makes it really easy to do stuff like

play02:47

this does have some catches with

play02:49

typescript though and we'll get into

play02:50

those in a little bit to achieve the

play02:52

same result with Redux toolkit you would

play02:54

have had to set up a new slice add the

play02:56

new slice to the root reducer and also

play02:58

set up the creation of the store and its

play03:00

provider if it hasn't already been set

play03:01

up with such stand you can access the

play03:02

count value in the increment action from

play03:04

the generated use count store hook you

play03:06

just call it we called create here set

play03:10

it as this value and now this value is a

play03:11

hook we can use wherever and we get all

play03:14

the values here we use count was just

play03:15

calling count store. count and then we

play03:17

increment it with count store. increment

play03:19

nice and simple since zustand mostly

play03:21

consists of a few react hooks its npm

play03:23

package is only 3.1 kiloby minified

play03:26

which is it's 44 times smaller than the

play03:28

base react package if you know react is

play03:30

quite small but react Dom which is where

play03:32

the bindings exist it's a bit larger

play03:34

it's 13 times smaller than Redux as well

play03:36

which is 40 kiloby minified to be fair

play03:38

Redux toolkit has a bit of additional

play03:39

bloat but it has to because Redux

play03:41

toolkit is the only way to make Redux

play03:43

usable I also like that he's not

play03:45

comparing it to vanilla Redux which is a

play03:46

show he's explicitly comparing to

play03:48

Redux toolkit good call refactoring

play03:51

Teemo to use zand since Teo is already

play03:53

using tanet query only had to rework the

play03:55

user authentication in profile storage

play03:56

to use a stand in the plane react

play03:58

version and the tanet query version this

play04:00

was done using react context refactoring

play04:02

this to zustan took around an hour in

play04:04

many cases it was almost a drop in

play04:05

replacement since I already used a hook

play04:07

to access the underlying context values

play04:09

let's take a look at this it's readable

play04:11

but not quite as readable as I would

play04:12

like so here we have the Teemo common

play04:15

hooks use user and we deleted that and

play04:18

replaced it with use user store nice and

play04:21

simple for a lot of places the tedious

play04:24

part of the refactor was changing the

play04:25

components in the common package I

play04:27

wanted to keep separate copies of Teemo

play04:29

with each of the libraries we took a

play04:30

look at so I set up the repo as a monor

play04:32

repo with each version stored as a

play04:34

separate folder to cut down on the

play04:35

duplicate code I also created a common

play04:37

package to store code that was used by

play04:38

several versions of Teemo sadly some of

play04:41

the components in the common package use

play04:42

react context they won't work with the

play04:44

sustained version of Teemo because I was

play04:46

using zustand instead of react context

play04:48

so I had to refactor these components to

play04:50

be pure and then refactor the previous

play04:52

versions to work with these changes I

play04:54

still kept the components that depended

play04:55

on context in the common package but

play04:58

moved them to a separate folder to show

play04:59

that they were not pure this is a very

play05:02

strange use case to be clear where you

play05:04

have three user experience-wise

play05:07

identical versions of the same app and

play05:09

you're trying to share components

play05:11

between them I find that sharing

play05:12

components between projects in general

play05:14

is kind of rough it's part of why I

play05:16

don't love component libraries and much

play05:18

prefer things like Tailwind where I can

play05:20

Define them myself or things like Shaden

play05:22

where they live in my code base it's

play05:23

pretty important to have your shared

play05:25

components be really really like

play05:27

low-level things so instead of using

play05:29

button your reusable component is button

play05:32

and then you wrap it with the user

play05:34

button that's somewhere in your

play05:35

application code so it doesn't surprise

play05:37

me this was somewhat painful because a

play05:38

lot of these components can't be pure

play05:41

and once you abstract non-pure

play05:43

components and they have expectations

play05:44

about things like which contexts are

play05:46

available falls apart fast so I'm

play05:48

not surprised this was painful but I'm

play05:49

pumped that he continued to push through

play05:51

it and make this work the other thing

play05:52

that stumped me for a moment was

play05:53

handling side effects in zustand it

play05:55

seemed to have the concept of middleware

play05:57

but I couldn't find any docs about how

play05:59

to write our own the docs only had

play06:01

instructions about using the middleware

play06:02

that came with the library why did this

play06:04

matter well since I was using HTTP only

play06:06

cookies to authenticate with the API I

play06:09

had to make a request to the API when

play06:10

the app started to verify whether the

play06:12

user was already logged in I could write

play06:14

an action using zustand to make the

play06:16

request but how could I run it whenever

play06:17

the app started ooh I'm going to share

play06:20

my weird solution to this problem and

play06:22

then I'm going to see how they solved it

play06:23

so what I recommend for these types of

play06:25

things if this is like a fundamental the

play06:28

application experience is based on

play06:30

whether or not this exists I wouldn't

play06:31

put this in the global use user store

play06:34

like this what I would do instead is use

play06:36

a context I know the whole point is

play06:38

we're moving off contexts but zust and

play06:40

contexts are actually quite useful for

play06:42

these cases so what I would have done is

play06:45

I would have created a context for this

play06:47

store and at the top level of the app I

play06:50

would have done with like react query or

play06:52

something the check to figure out if the

play06:54

user it's authenticated or not and then

play06:55

I would pass that to the create store

play06:57

function I would actually require this

play06:58

take a user in as part of its creation

play07:01

and pass it the user once we have it and

play07:03

then render something else entirely if

play07:05

we don't have a user but I would have

play07:07

code here in app at the root that

play07:09

actually figures out if you're

play07:11

authenticated or not and it generates a

play07:13

different state either the logged out

play07:16

State or the rest of the app with the

play07:18

store already instantiated if you're

play07:20

logged in this obviously only works if

play07:22

your store is dependent on that value

play07:24

and you don't want it if the user isn't

play07:26

signed in but I've used this pattern a

play07:28

ton especially in the Ping code base

play07:30

ping by the way is the video product

play07:31

that we built and went through YC with

play07:33

to make it easier for Content creators

play07:35

to do live collaborations there was a

play07:37

lot of complex state in this app around

play07:39

which devices are available so if I go

play07:40

to my room quick here there's a lot of

play07:42

complexity around which devices are

play07:45

available and once they're available

play07:47

which things can we use so the way that

play07:49

I handled this if we go back to the page

play07:52

here that one loading spinner is

play07:54

actually handling a couple different

play07:55

things the first layer is a check to see

play07:57

if you're authenticated to join this

play07:59

call or not then we check do you have

play08:01

the right permissions for devices like

play08:04

does the browser have permission to use

play08:05

video cameras and microphones on our

play08:07

page or not and once all of that is

play08:09

completed then we render the zustand

play08:12

store that actually manages your devices

play08:15

so the zustand store that keeps track of

play08:16

which devices you have and are available

play08:18

in the video and audio tracks they're

play08:19

creating does not get created that store

play08:22

does not exist until all the permissions

play08:24

are established so that's how I've

play08:26

solved this problem I actually find that

play08:28

nested zustan stores like that where

play08:30

they are created conditionally based on

play08:32

other things being met helps you escape

play08:35

from having to do a more complex State

play08:36

machine with all these different

play08:37

potentials the idea of a state machine

play08:39

that only exists when you are ready for

play08:40

it to might feel weird especially if

play08:42

you're treating it like a global but if

play08:44

you don't treat it like a global you're

play08:45

just using it as an API for defining

play08:47

what can be accessed where I found it

play08:49

was actually quite pleasant but now I'm

play08:51

curious how this author handles the

play08:53

problem and thank you agor for the shout

play08:54

out for paying it's both really cool as

play08:56

like a user of it but also with such a

play08:58

crazy expor ation of different things

play09:00

you can do in react and it definitely

play09:01

leveled up my react skills massively oh

play09:04

and in case this wasn't clear a zustan

play09:06

context means that this store is

play09:08

available in any children components so

play09:10

if you use this at a lower level like in

play09:12

the player in ping you can't access that

play09:14

store from above that component but you

play09:16

can from there and Below which I found

play09:18

to be a pretty nice way to manage these

play09:19

things in a lot of cases so there you go

play09:22

now you hopefully understand why these

play09:24

things work this way back to this the

play09:25

first solution that came to my mind was

play09:27

to create a custom wrapper component

play09:29

that had use effect hook to run the

play09:30

action but use effect hooks are evil and

play09:32

I try to avoid them as much as I can

play09:34

yeah I don't love this but then I

play09:36

realized we can interact with the

play09:37

zustand store outside of react so we can

play09:39

run the action immediately after the

play09:41

store is declared another plus of this

play09:42

approach is that the API request is only

play09:44

made once during development if we use

play09:47

the use effect hook instead like the

play09:48

previous version of Teemo the request

play09:50

would be made twice since react triggers

play09:51

use effect hooks twice when react strict

play09:53

mode is enabled during Dev yep I got

play09:56

into a long back and forth of Dan abov

play09:58

back in the day about this specifically

play10:00

because of the nature of nested uh

play10:03

stores and triggering specific events in

play10:05

this case connecting to a call I'm going

play10:07

to do the thing I never should do and

play10:09

actually look for the source code for

play10:11

this so here is how we handle the

play10:13

connection to your chat room in ping

play10:17

because in ping we have a little chat I

play10:18

should probably reopen that so in ping

play10:21

we have the little chat here where I can

play10:23

send

play10:24

messages it also keeps track of who's in

play10:27

the call which requests exist for people

play10:29

to join the call and a bunch of other

play10:31

random things like that that is handled

play10:33

through a service that I do not

play10:34

recommend called a uh I'm scared to say

play10:36

more because I legitimately think they

play10:38

might sue me they are one of the most

play10:39

toxic businesses I've ever worked with

play10:41

and I'm sure I'm going to get emails

play10:42

just for saying that much here anyways

play10:44

the room manager context provider here

play10:46

is a component that gets mounted when we

play10:48

know you're allowed in the room so we

play10:50

now know that you're authenticated to

play10:52

join that's part of the props that come

play10:53

in here I then use traditional react

play10:55

state to create a real-time store which

play10:58

is again A's way of connecting through

play11:01

via websocket to their store by default

play11:03

I don't actually instantiate it though

play11:05

because I have a use effect here which I

play11:07

use to create the realtime zustand store

play11:09

and then I set store to new store and

play11:10

now I have this store here in state and

play11:13

then I have a cleanup here that handles

play11:15

all of the different things you have to

play11:16

do to leave Ena because there is no one

play11:18

leave Ena you have to do like 15

play11:20

things and then I have a really nasty

play11:22

stringify to make sure that we only

play11:24

rerun when the config changes even

play11:26

though some of the keys in this are

play11:28

objects that might break this obnoxious

play11:30

but by far the easiest way to handle

play11:31

that don't do this don't ever do this it

play11:35

works but don't do it and then we return

play11:37

the real-time manager provider and the

play11:39

store for it is created using realtime

play11:41

store so what is this create realtime

play11:44

zustand store thing that's where the

play11:45

interesting stuff happens as somebody in

play11:47

chat was hinting at earlier you don't

play11:49

actually need to do this stuff in react

play11:51

and what was there is just how it's

play11:53

bound to react but the vast majority of

play11:55

the logic in particular where the state

play11:57

changes occur all happen happens outside

play12:00

of react in this file right here so in

play12:02

here we have a couple types we have the

play12:03

user presence data which keeps track of

play12:05

what the presence info which is which

play12:06

users are here and what devices do they

play12:08

have available we have the room manager

play12:10

store which has the abley client the

play12:12

public channels the presence channels

play12:14

which are like who is here and then the

play12:15

private channels which is something that

play12:17

I actually don't know how much we're oh

play12:19

it's for embeds or some things can't

play12:20

connect to those which is embeds but

play12:22

that's for private data messages things

play12:24

like that I have the provider in the

play12:27

store coming from the D in create

play12:29

context helper the realtime store config

play12:32

so this is the stuff that's specific to

play12:34

this store's existence we have the slug

play12:36

which is what it's connecting to connect

play12:38

private Channel which is should we

play12:39

connect the private Channel or not

play12:41

publish presentence which is do we

play12:42

publish the present state or not and do

play12:44

we use the embed process for

play12:46

authentication which is a different way

play12:47

of authenticating this is the stuff you

play12:49

pass in when you're instantiating the

play12:50

store then we have the create realtime

play12:52

zustand store helper function that I

play12:54

wrote it takes in that config it creates

play12:56

the able client it connects to the

play12:58

public Channel basically on the slug

play12:59

that we passed it attaches it then binds

play13:02

the analytics events it then catches uh

play13:05

if there's any errors and we were

play13:06

obsessed with analytics because a was

play13:08

breaking constantly and they were

play13:09

gaslighting us Non-Stop about it so we

play13:11

over logged and analyzed everything here

play13:14

so I could give them logs proving that

play13:16

their service was broken here we have

play13:17

public channel.on failed again obsessive

play13:19

logging because they don't give anyone

play13:21

way to know when it's broken I have the

play13:23

private Channel which is conditionally

play13:25

connected not if it's not so here we set

play13:28

let if we're supposed to connect we

play13:29

connect bind all of these things same

play13:32

with presents and here's where things

play13:34

get interesting we create the store and

play13:36

you'd think I would just return that

play13:37

right but that's where things get more

play13:39

fun if we keep scrolling I actually bind

play13:42

a bunch of things to the stuff that we

play13:44

created here because we created the

play13:46

store with all of these values but I

play13:47

also want to keep track of presence data

play13:49

I also want to know what messages were

play13:51

sent I have a lot of other things that I

play13:52

want that are not being handled here and

play13:55

I do that below because this store is an

play13:58

object that can interfaced with you

play14:00

don't just have to call this in react

play14:01

with a hook I call it down here with a

play14:04

store. set state so without even

play14:07

touching react at all I have a bunch of

play14:09

logic here that updates the store and

play14:11

updates the values in the store just by

play14:14

creating it because in this Creator

play14:15

helper function I bind all of these

play14:17

things after the creation occurs this

play14:19

isn't the only place we do this with

play14:21

ping we also do this for our video

play14:23

provider

play14:25

Agora similar pattern here where we

play14:27

instantiate the used state uh for the

play14:29

Agora store I use this create Agora call

play14:33

store helper set that to State and then

play14:36

we have the cleanup for if you leave or

play14:38

unmount the component we call the

play14:40

internal zust Gore provider we pass it

play14:42

the created store and then we can render

play14:44

our children and all of those will have

play14:45

access to this store and again if we go

play14:47

look at the logic here hop into

play14:49

store you'll see it's very similar Gore

play14:52

client create simple Goro client store

play14:54

vanilla create set and we return the

play14:57

Goro client call State the participants

play15:00

volume map all of these additional

play15:02

things but again afterwards where things

play15:04

get interesting I actually bind all the

play15:05

Agora listeners that keep the state up

play15:07

to date so here I new store. set State

play15:10

whenever new participants come in I just

play15:12

do this by looking at the vanilla client

play15:14

Because by the way the Agora client

play15:16

doesn't reliably let you know who is or

play15:18

isn't connected you have to ask it who's

play15:20

currently connected so I set up this

play15:21

helper to update the current connected

play15:23

participants and then I have all of the

play15:26

different events where a user joins or

play15:27

leaves bound to call this function so

play15:30

that the state machine gets updated when

play15:31

any of these events occur then I handle

play15:33

volume the volume handler was so hard

play15:36

because doing this in a way that didn't

play15:37

cause non-stop updates that made

play15:39

everything update constantly that was a

play15:41

bit painful but I have that here because

play15:45

we wanted to debounce and do whatever we

play15:46

could to keep track of those volume

play15:48

levels in a way that didn't suck and

play15:50

then the volume indicator is what

play15:51

triggers those updates now we have the

play15:53

connection State changed which if you're

play15:55

banned we will set the state to double

play15:58

join cuz that's the only time that uid

play15:59

ban occurred with the way we had things

play16:01

set up initially and that would set this

play16:03

specific State and then leave in those

play16:05

cases and then we return the store A bit

play16:07

chaotic but the value of this pattern is

play16:09

that I can do all of the different

play16:11

things that this client does in this

play16:13

case Agora I can set those all to update

play16:16

our state directly without having to do

play16:18

that with effects and react this all

play16:20

exists externally in vanilla JS and then

play16:23

zustand makes it really easy to consume

play16:26

this stuff within react so it basically

play16:29

Al lets us make this store do all of

play16:31

these updates write the new store.

play16:33

setstate calls in here and now I can

play16:35

reuse this code in other places other

play16:37

Frameworks wherever else but importantly

play16:39

this code still works in react because I

play16:41

can now call this as a hook so that's

play16:43

how we solve the problem and that's also

play16:45

quite different from using the use

play16:46

effect to update the store so the

play16:48

difference to be very clear is that in

play16:49

this example the user store always

play16:51

exists no matter what the user store

play16:54

exists and then these conditions are

play16:55

triggered to update the store to the

play16:58

state that the user wants it in what I

play16:59

did instead is the store is only created

play17:02

once we know this so if we have to

play17:04

connect via able if we have to

play17:05

authenticate the user we do any of those

play17:07

other things we do that then we use the

play17:09

results to determine if and how we

play17:11

should create the store so let's see how

play17:13

he handles it it's interesting how

play17:15

getting used to the react way leads you

play17:16

to overlook simpler Solutions in plain

play17:18

JS to the same problem it's actually a

play17:20

funny quote with what I just showed so

play17:22

here uh yeah we have the instantiation

play17:24

for the store and then just call use

play17:25

user store.get state. fetch user Yep

play17:28

this is basically what I'm doing just

play17:29

slightly higher level my impressions of

play17:31

zustand after refactoring Teemo to use

play17:33

zustand I have to say that I really like

play17:35

its generated hooks they're easy to get

play17:36

started with and they can easily be

play17:38

fine-tuned to prevent renders this makes

play17:40

it a perfect fit for managing state that

play17:41

has to be shared across several parts of

play17:43

a react app oh boy you found some good

play17:46

context let's watch this one I'm curious

play17:48

now the volume working oh God pre

play17:50

mustache Theo so ugly why did you guys

play17:53

ever let me look like that I'm sure

play17:55

y'all are having a lot of fun on Twitter

play17:59

making fun of Brin our old co-founder

play18:02

for the doesn't listen to Nickelback

play18:05

good days Classics good find ones in

play18:09

chat if I looked like that when you

play18:10

first started watching my

play18:13

content want to see some ones for the

play18:16

pre mustache

play18:17

crew before my videos were clickable

play18:20

that's a lot of y'all zeros if you came

play18:22

post mustache so I have that for

play18:23

comparison

play18:34

new today so zero yeah you're you're

play18:36

definitely post mustache if you're

play18:37

showing up today Zero by a month damn

play18:39

you were right on the line cool to see

play18:41

always interesting to see when and how

play18:42

people showed up here it was overdue

play18:44

hair cut no mustache the oh you

play18:46

were early early for those who aren't

play18:48

familiar

play18:50

plan here's OG needed a haircut badly

play18:53

but was too busy Theo back in the day

play18:56

this was even pryc if I recall yeah YC

play18:59

started in January of 2022 so this was

play19:02

so early so early anyways speaking of

play19:05

early I want to show some stuff in

play19:07

Dogecoin simulator because it's touched

play19:08

on here with these selects but I just

play19:10

want to show it in my own code base this

play19:11

is a dumb app I made during the Dogecoin

play19:13

Rush way back it's on GitHub and I want

play19:16

to show some fun things that I did in

play19:18

here so I have my game engine and this

play19:21

game engine is entirely built in Z stand

play19:24

and the game engine is pretty aggressive

play19:26

because things are updating constantly

play19:27

there's a tick that's firing multiple

play19:29

times a second and that triggers a bunch

play19:31

of other stuff to change like when I buy

play19:33

numbers go up and down there's a lot of

play19:35

things going on in this project that

play19:38

cause it to update State all over the

play19:39

place so I had to be pretty careful

play19:41

about where state does and doesn't get

play19:43

updated here's where I create the

play19:45

default State

play19:47

instance here's where I actually load it

play19:50

from Storage I save this all in local

play19:52

store and I have to abuse combine which

play19:54

is the only way to make zustand type

play19:56

safe at the moment to do a bunch of

play19:57

crazy here in order to create all

play19:59

of the updaters and states that actually

play20:02

cause this game to behave the way it's

play20:03

supposed

play20:05

to but what I want to showcase is the

play20:07

actual hooks that I consume things with

play20:09

I need to show how the selectors work

play20:11

first though so let's start there so see

play20:13

all of these used game store hooks your

play20:15

immediate thoughts probably why why not

play20:17

just call use game store and access all

play20:19

the keys off It Well by doing it this

play20:21

way I have selected just this state so

play20:24

if I was to delete all the other hooks

play20:25

here and just have this used game store

play20:28

where I grab state. USD this component

play20:30

will only update when USD changes if I

play20:33

just called use gam store then this

play20:35

component will rerun when anything

play20:37

inside of the game store changes and

play20:39

since we're selecting like this we're

play20:41

telling zustand hey quickly before you

play20:43

update my component get this key out of

play20:45

the object and check if that changed the

play20:48

diff isn't happening on the entirety of

play20:50

game store the diff is happening on the

play20:52

resulting selector here which is what

play20:54

was cool about the thing I was just

play20:55

showing with the um game engine if I go

play20:58

back

play20:59

there yeah not only can you select this

play21:03

way but you can also calculate things

play21:04

and do additional math and work here and

play21:07

as long as what you return here is

play21:09

representative of if an update should

play21:10

occur or not because it's different from

play21:12

the previous value if it's different it

play21:14

will trigger an update if it's the same

play21:15

it won't which is super super handy for

play21:19

just dumping a bunch of in state

play21:21

like I did here there is so much stuff

play21:22

going on in the store but now you only

play21:24

have to access and Trigger updates on

play21:26

the parts that you want to go back to

play21:27

the article I'm sure he's about to cover

play21:28

what I just showed here yeah we have the

play21:31

count store and then we have cons count

play21:32

use count store State state. count so

play21:35

this will trigger when the count changes

play21:36

if we go back to how this store was

play21:37

defined up here we have two things count

play21:40

which is a value that changes and

play21:41

increment which is a function that

play21:42

theoretically shouldn't change so if you

play21:44

have a button component for increment

play21:46

that doesn't know the count it shouldn't

play21:48

update when the count changes and you

play21:50

can be sure of that very easily by

play21:52

making the increment call to use count

play21:54

store but we actually select the

play21:56

increment key and since we did this this

play21:58

will never trigger a rerender because

play22:01

the function never changes I also like

play22:03

how zustand tackles async operations is

play22:05

just calling set with the result then

play22:07

the action can be run in a synchronous

play22:09

context no middleware no promise chains

play22:11

this is what I was showing before with

play22:12

all of our Agora Anda stuff where there

play22:14

a lot of that was promises and then on

play22:15

the dot thens we would trigger whatever

play22:18

behaviors we wanted to so we have this

play22:19

fetch user we try con users await get

play22:22

user then we set the value if there's an

play22:24

error then we set it to the error

play22:27

state so the biggest benefit of

play22:29

switching away from react context the

play22:31

biggest issue with using context to

play22:33

share State across several components is

play22:34

that any change to the state will cause

play22:36

all subscribed channels to render

play22:38

moreover changing the context value also

play22:40

causes child components within the

play22:42

context provider to render since the

play22:43

context provider usually wraps the

play22:45

entire app the entire app ends up

play22:47

rendering Teemo used to have this

play22:49

problem in the profile screen where

play22:50

saving the user details would cause the

play22:52

entire app to render you could memorize

play22:54

to get out of that I almost want to read

play22:56

the source code oh he called it out here

play22:59

you can solve this with memorization in

play23:00

multiple contexts but it's tedious but

play23:02

by using a state management Library like

play23:03

zustand you can avoid these issues more

play23:05

easily zustand doesn't require you to

play23:07

wrap the entire app with a provider

play23:08

component or Hawk and it sends updates

play23:10

directly through the to the generated

play23:11

hooks so the entire app doesn't render

play23:13

when the state changes since you can

play23:15

also subscribe to specific State values

play23:16

and actions with usand you can also make

play23:18

components render only when the state

play23:20

relevant to them changes you know

play23:22

what I want to prove that this isn't

play23:24

that big a deal does he have source code

play23:26

somewhere he does okay react

play23:30

Source

play23:36

app.jsx okay I found the problem almost

play23:38

certainly but let's confirm where is

play23:41

that coming from is the user context

play23:43

provider one of the common packages it

play23:45

is okay common context user context

play23:47

provider uh ding ding

play23:49

ding this should be memorized the

play23:51

problem is that the children are going

play23:54

to re rerun because there is no

play23:56

indication that this can be treated

play23:58

statically

play23:59

if you were to memorize children before

play24:00

rendering it you could avoid almost all

play24:03

of the reenders here I'm pretty

play24:06

certain yeah I'm almost positive that

play24:08

would fix this do I clone it to prove

play24:11

that do we make this already too long

play24:13

video far too much longer so I'm going

play24:15

to save this yeah see that the whole

play24:18

page update that's the thing I think we

play24:20

can nuke let's go straight in to test

play24:23

that so my hypothesis is that this isn't

play24:27

memorized proper

play24:29

only one way to find out though which is

play24:31

to go all the way up here to the user

play24:33

context provider con memo

play24:39

children and do that not sure if it's

play24:41

smart enough to handle the hot reloading

play24:43

for that so I'm going to root Force

play24:45

resetting of those now I'm curious Tes

play24:49

Theo V2 I'll change this to

play24:53

L save it's still updating the

play24:56

whole thing really

play24:59

a no it's not was it before though

play25:03

should probably have confirmed that in

play25:05

the console ahead of time user context

play25:09

provider

play25:14

children H it's not

play25:17

interesting so what is causing

play25:19

everything to render then how is it

play25:21

being

play25:22

consumed if we go to that's common where

play25:25

I need to be is the profile

play25:29

component oh that's why con user is use

play25:32

user that would do it yeah we only want

play25:35

clear user from this so that's the issue

play25:38

is that we're selecting way more than we

play25:40

want from this hook and that triggers

play25:42

updates that we don't actually want to

play25:44

have happen that would not be fun to

play25:47

optimize for I Now understand the pain

play25:51

yeah if this context is being used for

play25:54

all of these values as well as for

play25:56

exposing that specific function

play26:00

that sucks ass yeah never mind why did I

play26:02

ever doubt the author clearly they have

play26:04

a case here that sucks yeah it's

play26:06

basically impossible with context to

play26:08

select just one thing so in this case

play26:10

what we want isn't the whole use user we

play26:13

just want clear user which is a function

play26:15

that theoretically should never change

play26:17

but sadly because use user is a context

play26:21

that provides all of these other things

play26:23

in it it has the user object it has the

play26:27

authentication state it has additional

play26:29

values and all of this is part of what

play26:31

this provider is giving us that's why

play26:33

this sucks theoretically we could wrap

play26:35

this with another provider that's like

play26:38

user context actions provider that is

play26:40

just the functions which should never

play26:41

change and then in here we could just

play26:43

consume that and be good do I do the

play26:46

miserable thing of that it feels hellish

play26:48

but I think I can do it relatively quick

play26:51

source packages react Source yeah we

play26:55

have it here as well with customize user

play26:58

we have it at the top of profile which

play27:00

is where we just were we have it in

play27:01

change password as

play27:03

well yeah I see the problem now so the

play27:06

reason zustan is super useful for

play27:08

something like this is if this was like

play27:09

a used store then I could have store

play27:13

store do user. clear user and now I

play27:17

could select just the clear user

play27:19

function and ignore everything else

play27:20

which keeps it from having to update

play27:22

when it shouldn't but this example seems

play27:24

to be a lot more ironed out than I was

play27:25

initially thinking there when I had my

play27:27

initial push back yeah the suain version

play27:29

when you save only updates specific

play27:32

parts of the page instead of forcing the

play27:34

whole profile to reender because if we

play27:36

look at the code I'll just go to the

play27:37

same place in the zustand version

play27:39

zustand Source routes profile. jsx use

play27:44

user store State state. clear user and

play27:47

now we just have the clear user function

play27:49

no additional stuff has to be done we

play27:50

have to create a new function here we

play27:51

can just pass that to button and it

play27:53

works dope actually a really good

play27:56

example nice having the three different

play27:57

solutions in one repo to compare if

play27:59

you're curious about the difference

play28:00

between vanilla react react plus react

play28:02

query and zustand you have a great place

play28:04

to look to compare all of those sus

play28:06

thing it's a thumbs up from me it's a

play28:07

lightweight package that is well thought

play28:09

out and approachable especially if

play28:10

you're familiar with Redux and the flux

play28:12

and Redux pattern this is probably why

play28:13

the community's fallen in love with it

play28:15

what if you hate the flux pattern and

play28:16

anything else like it the next month's

play28:17

post let's take a look at the atomic

play28:19

patterns for managing state which also

play28:20

come from Facebook and meta as well as

play28:22

Jodi which is taking over from recoil as

play28:24

the DEA facto library for this pattern

play28:26

fun fact both Jodi and zust stand are

play28:29

part of the point Manders Collective pmn

play28:31

DRS they are legends the amount of cool

play28:34

they build is unbelievable this is

play28:36

where react 3 fiber came from where

play28:38

react spring and use gesture came from

play28:40

where a ton of crazy 3D stuff came from

play28:43

it's also zustand and Jodi as

play28:47

well great stuff yeah one of the best

play28:50

sources of good react Solutions and also

play28:53

a great resource for General full stack

play28:55

typescript problem solving awesome

play28:57

project awesome crew awesome stuff that

play28:59

they're building any opportunity I get

play29:00

to shout them out I take also my vs code

play29:02

theme which I get asked about all the

play29:04

time if you're this late into the video

play29:06

I'll let you know the secret this is the

play29:07

PO Anders theme yeah this was great

play29:10

shout out to veros for writing this it

play29:11

is a great post and I will definitely be

play29:13

here to see the Jodi one that you update

play29:15

in the future check out his blog if you

play29:17

want to hear more about the uh if you

play29:18

want to hear more about the react

play29:19

context version as well as the react

play29:21

query version the links are all in the

play29:22

description check out the blog and until

play29:24

next time peace Nars

Rate This

5.0 / 5 (0 votes)

Etiquetas Relacionadas
ReactZustandState ManagementPerformanceHooksReduxFlux PatternTypeScriptOptimisationReact Query
¿Necesitas un resumen en inglés?