petesellsmihouses.com

Effortlessly Manage State Updates in Zustand with Immer

Written on

Chapter 1: Understanding Zustand and Immer

In managing complex state, especially with deeply nested objects, Immer can significantly simplify the process. In this article, we'll illustrate how to incorporate it through a practical example, building on concepts from our previous discussion about selectors.

Installing Required Packages

To begin, you’ll need to install the necessary packages:

npm install immer

We will design a feature that allows users to select a character and lock it. Once locked, users cannot choose a different character until they unlock the current one. Make sure to wrap your store logic using Immer.

import { create } from "zustand";

import { createSelectors } from "@/store/createSelectors";

import { immer } from "zustand/middleware/immer";

// Your code here...

export type SelectedCharacters = Character & { // New property

locked: boolean;

};

type CharactersStore = {

// Your code here...

selectCharacter: (character: Character) => void;

character: SelectedCharacters; // New property

lockCharacter: () => void; // New method

unLockCharacter: () => void; // New method

};

export const useCharacters = createSelectors(create(

immer((set) => ({ // New function

// Your code...

character: undefined,

selectCharacter: (character: Character) => set(state => {

if (state.character?.locked) {

return state;

}

return ({

...state,

character: {

...character,

locked: false,

},

});

}),

lockCharacter: () => set(state => ({

...state,

character: {

...state.character,

locked: true,

},

})),

unLockCharacter: () => set(state => ({

...state,

character: {

...state.character,

locked: false,

},

})),

}))

));

Creating a New Component

We will utilize store selectors to obtain character data and implement an event handler for locking and unlocking characters, similar to the logic seen in the Digimon and Pokemon components.

"use client";

import Image from "next/image";

import { useCharacters } from "@/store/use-characters";

import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";

import { Button } from "@/components/ui/button";

export function SelectedCharacter() {

const character = useCharacters.use.character();

const lockCharacter = useCharacters.use.lockCharacter();

const unLockCharacter = useCharacters.use.unLockCharacter();

function toggleCharacter() {

if (character.locked) {

unLockCharacter();

} else {

lockCharacter();

}

}

if (!character) {

return null;

}

return (

<Card>

<CardHeader>

<CardTitle>Selected Character</CardTitle>

</CardHeader>

<CardContent>

<h2>{character.name}</h2>

<Button onClick={toggleCharacter}>

{character.locked ? "Unlock" : "Lock"}

</Button>

</CardContent>

</Card>

);

}

Updating the Digimons Component

In this section, we’ll place the selection button within the card content. Ensure that you include the selectCharacter selector alongside existing selectors.

const selectCharacter = useCharacters.use.selectCharacter();

selectCharacter(digimon)}>Choose

Updating the Pokemons Component

We will apply the same methodology for the Pokemon component as we did for the Digimons.

const selectCharacter = useCharacters.use.selectCharacter();

selectCharacter(pokemon)}>Choose

Testing the Implementation

Let’s examine the current state of our selections.

Where Does Immer Fit In?

You're right to ask where Immer comes into play. We simply wrap our logic with Immer, keeping state updates straightforward. Immer enables us to modify specific properties directly without affecting the entire state object. Let’s update the store:

selectCharacter: (character: Character) => set(state => {

state.character = {

...character,

locked: true,

};

}),

lockCharacter: () => set(state => {

state.character.locked = true;

}),

unLockCharacter: () => set(state => {

state.character.locked = false;

}),

Final Testing

Everything should function as expected.

Conclusion

With Immer, you can avoid copying the entire state, allowing for targeted updates on specific properties, which is especially beneficial when managing complex structures. Remember, using Immer is optional; you can choose to implement it based on your needs.

Full Code Repository: [GitHub Link]

Thank you for engaging with this article. I hope you found it insightful! If you have any feedback or thoughts, feel free to reach out.

In Plain English 🚀

Thank you for being part of the In Plain English community! Before you leave, consider following us for more updates: X | LinkedIn | YouTube | Discord | Newsletter. Explore more content on Stackademic | CoFeed | Venture | Cubed, and find additional resources at PlainEnglish.io.

Chapter 2: Video Tutorials

In this chapter, we will provide video resources to enhance your understanding of Zustand and Immer.

This video demonstrates how to utilize Zustand with persistent storage for efficient state management in Next.js.

Quick guide on implementing Zustand State Management in just 5 minutes, tailored for Next.js users.