Open Rope-a-dope opened 4 months ago
itemsStore.js
import { create } from "zustand"; import { persist } from "zustand/middleware"; import { initialItems } from "../lib/constants"; export const useItemsStore = create( persist( (set) => ({ items: initialItems, // Separate Actions from State actions: { addItem: (newItemText) => { const newItem = { id: new Date().getTime(), name: newItemText, packed: false, }; set((state) => ({ items: [...state.items, newItem] })); }, deleteItem: (id) => { set((state) => { const newItems = state.items.filter((item) => item.id !== id); return { items: newItems }; }); }, toggleItem: (id) => { set((state) => { const newItems = state.items.map((item) => { if (item.id === id) { return { ...item, packed: !item.packed }; } return item; }); return { items: newItems }; }); }, removeAllItems: () => { set(() => ({ items: [] })); }, resetToInitial: () => { set(() => ({ items: initialItems })); }, markAllAsComplete: () => { set((state) => { const newItems = state.items.map((item) => { return { ...item, packed: true }; }); return { items: newItems }; }); }, markAllAsIncomplete: () => { set((state) => { const newItems = state.items.map((item) => { return { ...item, packed: false }; }); return { items: newItems }; }); }, }, }), { name: "items", partialize: ({ state }) => ({ state }), // only persist state } ) ); // exported - consumers don't need to write selectors. Prefer atomic selectors export const useItems = () => useItemsStore((state) => state.items); // 🎉 one selector for all our actions. As actions never change, it doesn't matter that we subscribe to "all of them". The actions object can be seen as a single atomic piece. export const useItemsActions = () => useItemsStore((state) => state.actions);
AddItemForm.jsx
import { useRef, useState } from "react"; import { useItemsActions } from "../stores/itemsStore"; import Button from "./Button"; export default function AddItemForm() { const [itemText, setItemText] = useState(""); const inputRef = useRef(); const { addItem } = useItemsActions(); const handleSubmit = (e) => { e.preventDefault(); // basic validation if (!itemText) { alert("Item can't be empty"); inputRef.current.focus(); return; } addItem(itemText); setItemText(""); }; return ( <form onSubmit={handleSubmit}> <h2>Add an item</h2> <input ref={inputRef} value={itemText} onChange={(e) => { setItemText(e.target.value); }} autoFocus /> <Button>Add to list</Button> </form> ); }
Counter.jsx
import { useItems } from "../stores/itemsStore"; export default function Counter() { const items = useItems(); const numberOfItemsPacked = items.filter((item) => item.packed).length; const totalNumberOfItems = items.length; return ( <p> <b>{numberOfItemsPacked}</b> / {totalNumberOfItems} items packed </p> ); }
itemsStore.js
AddItemForm.jsx
Counter.jsx