Is one of the important keys in every application to have better UX and make users happier (of course you may not care about users happiness, but it makes yourself happier 😃 Because you want to do something in a best way and you enjoy it). Performance is different on the web based applications it depends on the tools you choose and how you use them specially on low memory devices like TVs and Mobiles.
There are a lot of libraries out there that you just install and use them in your project. Some people just using the Context Api it self which is powerful and easy to use, but also has some downsides like unnecessary re-renders, Take a look at this example:
As you can guess, All the nested components get re-render, You are thinking that I’m wrong I can use useMemo() or split the component in two and use memo() , But everything has a cost so better to avoid them! Checkout this interesting article here from Kent C. Dodds If you see the console.count() results in the console then it means React started the reconciliation process to detect the diffs there in virtual dom. Sometimes this process is not fast enough and you will see the issues and it becomes slow by decreasing frames, sometimes lags in animation like close a Modal. Again checkout this article from Kent C. Dodds to understand better these downsides in React.
This is just small example and you won’t see the affects but imagine you have thousands of components there. It was really important to me to avoid these kinds of unnecessary re-renders in the application and only update the part that really needs to be update.
Today going to talk about the Xstate and It benefits to share the state between the application.
And a simple example:
Let’s say we have a music streaming application that user can add/remove items from the library, So the flow will be:
- Getting User Profile Information (also on refreshing page to detect if user is already logged in)
- Getting User Library Information (Musics, Videos, Podcasts and more…- We need this information everywhere not only in one page)
This sounds really easy and straght forward without Xstate, Isn’t?
It’s possible but it’s not good and clean enough, So the answer is a NO :
- You need to define the states yourself
- You need to define the loadings states yourself
- Your logic and your components getting mixed together, A lots of useEffect(), useState(), useCallback, Promise().then().catch() and callbacks
- You need to create guards in all the actions, For example you need to check if the latest library is loaded then you can add or remove to/from library
- A lots of setState since the datas need to be store somewhere and need to be reactive, This means a lots of re-renders
- Hard to debugging since you don’t know the states
- Hard to have control on the flow
I’m sure there are more.
Behind the state management Xstate gives you powerful control on the flow and different states. If you’re not familiar with the Xstate first take a look at the documents since this post is not about learning Xstate it’s about using it.
THIS IS REALLY COOL! You can define all the steps in the machine and transition between them using events or states itself by target key.
In a normal way there are some helpers from @xstate/react package, like useMachine() :
All is good, We have 3 different components:
- LibraryHandler: It doesn’t needs to be update at all since it’s only injected actions.
- LibraryPresent: It only needs to be update when the library.mp3s item has been updated in the store.
- UserProfile: It only needs to be update when user item has been updated in the store.
But we didn’t solve the issue yet, We still will get those nasty unnecessary re-renders and we wan’t to avoid using memoize helpers. So we need atomic updates, We need to run the machine without passing the state to provider, Here is the place that interpret and useService hook will shine:
The authService function will return the service, Eeach service has some useful methods but what we need here are:
- start(): to start a service
- stop(): to stop a service
- send(): to send events
- subscribe(): to listen to changes
We need to update our AppProvider component and provide the service instead of state directly.
So far so good! You see selectors object above that I’ll explain it in a bit.
Let’s create the subscriber hook:
This hooks takes two main arguments:
- service: A service that has been started
- selector: A function that receive the state and returns a specific data
What’s going here?
It’s easy instead of having one global subscriber we have multiple subscribers, And when the component unmounted we unsubscribe the service. This is kind same as redux selectors working.
Let’s update the components that needs the data.
Yes this is exactly what we need to get out of the re-render hell in React apps with xstate.
xstate is definitely one of the coolest thing that I have ever worked with it in our applications. It’s helpful for managing the complicate states there are a lot of cooler things that you can do with it. shout out to David Khourshid for creating it.
Thanks for reading, If there is anything I have missed or if there is a better way to do something, Then please let me know. I’d be happy 🤓