This tutorial is a 3-part series and will give a detailed walk through of how to do state management on the component level with Zustand. We'll show how to use Zustand in a tactically relevant way, while creating a fully integrated React component.
Here's a breakdown of what we'll be covering throughout the series:
As a caveat, each part above is linked to a Code Sandbox, complete with the section's fully completed code, for convenience. To make the most use of your time while following this tutorial, we recommend opening and forking the part's sandbox at the beginning of the section in a separate tab. Our Code Sandbox example can be your 'target repo'. While you're completing each part of the tutorial, your goal should be to write code that eventually resembles the target.
This part has the same prerequisites as Part 1.
Frontends can be stubborn - and a huge headache - so let's try to remove as much complexity as possible. In this part of the tutorial, we're going to explore Zustand and create our data models, create our store and hook them up to our React component that we set up in Part 1.
Zustand is a flexible state management system. Here’s how the creators of Zustand describe the tool:
“A small, fast and scaleable bearbones state-management solution. Has a comfy api based on hooks, isn't boilerplatey or opinionated, but still just enough to be explicit and flux-like. Don't disregard it because it's cute. It has quite the claws, lots of time was spent to deal with common pitfalls, like the dreaded zombie child problem, react concurrency, and context loss between mixed renderers. It may be the one state-manager in the React space that gets all of these right.”
To start connecting Zustand, we’ll need to NPM install Zustand.
Since we're going to be using Zustand to load the component's content dynamically, we always want to have backup content available in case there's any unexpected issue loading data. We'll house this backup content in our fallback.js file.
Head to the src/components/email-block folder and add your fallback.js file here. Once the file is generated, add the following code snippet:
We're going to control the value of the component's button in accordance with its state. Once our component is loading, it can be in one of the following four states 1) initial 2) processing 3) success or 4) failed
Head to src/components/email-block folder and create a logic folder. Inside this folder, create the following files: data-models.js, flows.js and store.js. To keep things clean, it's important that these three files exist independently. Your current folder structure should look like this:
A model is nothing more than a piece of data that we want to manage across the application. As a professional preference, we like to separate the data models from the store. For this app, we'll need two data models - the content model and the loading model.
Let's start by tackling the content model, which will be responsible for the Title, Subtitle and Button Text. In the model, we're going to need:
Head to the src/components/logic/data-models.js file and add the following code snippet:
In this code, we are creating an initContentModel function to help us organize our Zustand store. At this stage, we are using the content from the fallback.js file as the default value. We'll be changing this to dynamic data later on in the tutorial.
While we're here, let's tackle the loading model, which will be responsible for loading and processing. Loading is used when we are requesting data from a server and Processing is used for when we are sending data to a server. In the model, we're going to need:
In the same src/components/logic/data-models.js file, expand the file using the following code snippet:
For clarity's sake, let's first define what a store actually is before doing anything else. According to Zustand documentation, stores are simply hooks. Here's a snippet from the NPM page:
We're now going to create our Zustand store and add our previously created data models. To do this, head to the src/components/email-block/logic/store.js file and add the following code snippet:
We are using the dev tools to help us with debugging. For more on Zustand's dev tools, visit Zustand's documentation on dev tools:
Congrats! Your store is now created 🎉
To connect the store to the component, it's as easy as using a hook. Head back to src/components/email-block/EmailBlock.js and import useStore from the src/components/email-block/logic/store.js file.
We will use useStore to grab the content, the loading state and the processing state.
At this point, you should see the text being pulled successfully from our fallback.js file. We can also test the connection by setting the loading initial state to true and confirming that the component UI is actually showing the skeleton loader.
Now that we have our UI fully connected to the Zustand store, we can manage the button dispatch. As a caveat, our professional preference is to organize any user triggered event in a flows.js file. This will allow us to transition the button state following a user event, like a button click:
Let's create a wait function to simulate a network delay. Inside the flows.js file, add this code snippet:
In the same file, let's create an empty function useDispatchEmailFlow that will return an async dispatch function.
Once that's done, update the useDispatchEmailFlow function with the following code:
As you can see, inside the use useDispatchEmailFlow we're using the Zustand store to grab the functions setProcessing, clearProcessing, setButtonText and the content of the button. Inside the dispatch function, we're simulating a network call using the wait function.
Now, when you click on the button, you should see the button get disabled, transition states, then reset. Pretty cool, right?!
That’s it! In this part of the series, you learned how to set up data models, create a store and connect it to a React component.
Head to Part 3 of this tutorial where we will create the backend logic and microservice data resources for our web app. Our goal will be to complete and deploy everything in less than 20 minutes.
You can find the app’s finished up until this point on this Code Sandbox.