Zustand is an excellent alternative to Redux if you want a simpler, yet powerful solution to manage state in your React projects. In this post, we'll be going over how to install and use Zustand in your Expo app.
This is the project structure for this blog post. I'm using Expo Router to utilize file-based routing. I'm also using the createBearStore from the Zustand docs for a uniform experience between the docs and this post.
root
> app
> report-sighting
index.tsx
_layout.tsx
index.tsx
> assets
> state
index.ts
> node_modules
... rest of files
This app has two pages, index.tsx (Home) and report-sighting/index.tsx (Report Sighting). The _layout.tsx in the app directory holds a Stack layout to easily navigate between the pages.
Install Zustand
To install Zustand in your app, run the code below to add it into your Expo app.
npx expo install zustand
If you're unfamiliar with Expo libraries, using npx expo install
instead of npm install
might be a little odd. Using npx expo install
enables Expo to pick a version of the library that is compatible with your project. One of the many ways Expo helps limit complexity in your project. Since this isn't a blog on how to use Expo specifically, I'll leave you to read any documentation you need.
Create the useBearStore hook
In our index.ts
file inside the state
folder, we'll create the hook to manage state.
import { create } from "zustand";
// create an interface for the bear store to implement
interface BearStore {
bears: number;
increasePopulation(): void;
}
// create the bear store, implementing the BearStore interface
const useBearStore = create<BearStore>((set) => ({
// set the initial value in the store to 0 bears
bears: 0,
// When this function is fired off, it increases the number of bears by 1
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
}));
// export the useBearStore hook
export default useBearStore;
Here, we're creating an interface BearStore
to give our useBearStore
the minimum required properties required for our state-management. We'll need a number type, bears
that holds the amount of bears seen and a void function increasePopulation
that increments the bears count by one when called.
Display the total bear sightings
Next, in the index.tsx
file in the app
directory, we'll create a <View>
that displays the total number of bears seen.
import { Link } from "expo-router";
import { View, Text, StyleSheet } from "react-native";
import useBearStore from "../state";
export default function Home() {
// grab the total number of bears from the useBearStore hook
const bears = useBearStore(({ bears }) => bears);
// return the style for the home page
return (
<View style={styles.wrapper}>
<Text style={styles.heading}>Total sightings: {bears}</Text>
<Link style={styles.link} href="/report-sighting">
Report a sighting
</Link>
</View>
);
}
// stylesheet for the Home page
const styles = StyleSheet.create({
wrapper: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
heading: {
fontSize: 30,
marginBottom: 20,
},
link: {
backgroundColor: "#333",
color: "#fff",
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 5,
},
});
In order to display the total number of bears seen, we need to grab the bears value from the useBearStore
. Here, we destructure the state object stored in useBearStore
and return only the bears, which happens to be the total number of bears stored in state. We store the bears in the variable const bears
. Just like any variable in React, we use it in the return
value surrounded by curly brackets.
Updating the total number of bears
The last thing we need to implement (for this custom hook) is our ability to update the state. We'll be adding our code in the index.tsx
file in the app/report-sighting
directory.
import { View, Text, StyleSheet, Pressable } from "react-native";
import useBearStore from "../../state";
export default function ReportSighting() {
// grab the increasePopulation function from the useBearStore hook
const increasePopulation = useBearStore(({ increasePopulation }) => increasePopulation);
// return the view for the Report Sighting page
return (
<View style={styles.wrapper}>
<View style={styles.buttonWrapper}>
<Pressable onPress={increasePopulation}>
<Text style={styles.button}>I've seen a bear!</Text>
</Pressable>
</View>
</View>
);
}
// stylesheet for the Report Sighting page
const styles = StyleSheet.create({
wrapper: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
buttonWrapper: {
width: "auto",
backgroundColor: "#333",
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 5,
},
button: {
color: "#fff",
},
});
Just like when we grabbed the bears from our hook, we'll grab the increasePopulation
function from useBearStore
. In order to call the increasePopulation
function, we have a <Pressable>
element that fires off the function when it's pressed.
Wrapping up
Zustand manages state across the entire application. The project is setup with two unrelated pages, yet the state is managed across both pages. One page displays the current amount of bears seen and the other page updates the total count. This is a very simple example of how global state-management works, but hopefully you can see the power of Zustand and just how simple it is to manage state.
You can visit my GitHub if you want the full working code to play around with.