[Zustand] Zustand๋ก ์ํ๊ด๋ฆฌ๋ฅผ ํด๋ณด์!
๋ง์ ์ํ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค ์์ฆ ๋ ์ค๋ฅด๋ 'Zustand'.
npm trends ์ฌ์ดํธ๋ฅผ ๊ฐ์ ๊ฒ์์ ํด๋ณด๋ ์ญ์ 1์๋ redux์ง๋ง ๋๋๊ฒ๋ 2์๊ฐ Zustand๋ค.
2๋ ์ ๋งํด๋ recoil์ด ๋ง ๋ฌ๋ค. ๋ผ๋ ์๋ฌธ์ด ์์ด์ ์ฌ์ฉํด๋ดค๋๋ฐ... ์ด์ ์ ๋ฐ์ ์๋ค..
(recoil ๋ง๋์ ๊ฐ๋ฐ์๋ถ๋ ํด์ฌํ๋ค๋ ์์์ด......)
๊ทธ๋์ Zustand๊ฐ ์ ์ธ๊ธฐ๊ฐ ์๋๋ฐ? ๋ฌด์จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ๋ฐ?
Zustand๋ ๋ ์ผ์ด๋ก '์ํ'๋ผ๋ ๋ป์ ๊ฐ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก,
๊ฐ๊ฒฐํ ํ๋ญ์ค(Flux) ์์น์ ๋ฐํ์ผ๋ก ์๊ณ ๋น ๋ฅด๊ฒ ํ์ฅ ๊ฐ๋ฅํ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
ํ ๊ฐ์ ์ค์์ ์ง์ค๋ ํ์์ ์คํ ์ด ๊ตฌ์กฐ๋ฅผ ํ์ฉํ๊ณ ๋ณด์ผ๋ฌ ํ๋ ์ดํธ๊ฐ ์ต์ํ๋์ด ์์ด ์คํ ์ด ํํ์์๋ ๊ต์ฅํ ๊ฐ๋จํ๊ฒ ์ํ๊ด๋ฆฌ๋ฅผ ํ ์ ์๋ค.
๋ํ,
- useSelector hook์ ์ ๊ณตํ์ฌ ์ปดํฌ๋ํธ๊ฐ ํ์ํ ์ํ๋ง ์ ํ์ ์ผ๋ก ๊ตฌ๋ => Provider๋ก ๊ฐ์ ํ์ X
- memoization์ ์ฌ์ฉํด ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋ ๋๋ง๋ค ์ปดํฌ๋ํธ ์ฌ์คํ์ ๋ฐฉ์ง => Context API๋ณด๋ค ๋ฆฌ๋ ๋๋ง ๊ฐ์
๊ทธ๋ ๋ค.
redux๋ฅผ ์ฌ์ฉํด๋ณด๋ฉด ๋ง์ ์ฅ์ ์ด ์์ง๋ง ์ ์ด์ผํ๋ ์๋ง์ ๋ณด์ผ๋ฌ ํ๋ ์ดํธ ์ฝ๋์ ๋ค์ ํผ๋ก๊ฐ์ ๋๋ผ๊ฒ ๋๋ค.
๊ทธ๋ฐ ๋จ์ ์ ๋ณด์ํ๋ ์ญํ ์ ํ๋ ๊ฒ์ด Zustand์ธ ๊ฒ๊ฐ๋ค.
Zustand๋ฅผ ์ฌ์ฉํด๋ณด์
์ค์น๋ฐฉ๋ฒ์ ๊ฐ๋จํ๋ค.
# NPM
npm install zustand
# Or, use any package manager of your choice.
์ค์น๋ฅผ ํ๋ฉด store.js ํ์ผ์ ์์ฑ ํ, create ํจ์๋ฅผ ํตํด store๋ฅผ ์์ฑํด์ฃผ๋ฉด ๋๋ค.
import { create } from 'zustand'
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
updateBears: (newBears) => set({ bears: newBears }),
}))
์คํ ์ด๋ ์ํ ๋ณ์์ ํด๋น ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๋ ์ก์ (ํจ์)์ผ๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
์ด๋ ๊ฒ ๋ง๋ ์ํ ๋ณ์์ ์ก์ (ํจ์)๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ํด๋น ์ปดํฌ๋ํธ์์ useStore ํจ์๋ฅผ ํธ์ถํ๋ฉด ๋๋ค.
import { useStore } from "./store/store";
function BearCounter() {
const bears = useStore((state) => state.bears)
return <h1>{bears} around here...</h1>
}
function Controls() {
const increasePopulation = useStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button>
}
Zustand๋ฅผ ํ์ฉํ Todo App
store.ts
import { create } from "zustand";
interface TodosType {
id: number;
text: string;
isDisabled: boolean;
isChecked: boolean;
}
interface Store {
Todos: TodosType[];
addTodo: (todo: string) => void;
remove: (id: number) => void;
updateDisabled: (id: number, disabled: boolean) => void;
updateChecked: (id: number, check: boolean) => void;
update: (id: number, newText: string) => void;
}
//store ์์ฑ
export const useStore = create<Store>((set) => ({
Todos: [{ id: 0, text: "๊ณต๋ถํ๊ธฐ", isDisabled: true, isChecked: false }],
addTodo: (todo: string) =>
set((state) => {
const lastId = state.Todos.length > 0 ? state.Todos[0].id : 0;
const newId = lastId + 1;
const newTodo = { id: newId, text: todo, isDisabled: true };
return { Todos: [newTodo, ...state.Todos] };
}),
remove: (id: number) =>
set((state) => ({ Todos: state.Todos.filter((v, i) => v.id !== id) })),
updateDisabled: (id: number, disabled: boolean) =>
set((state) => ({
Todos: state.Todos.map((todo) =>
todo.id === id ? { ...todo, isDisabled: disabled } : todo
),
})),
update: (id: number, newText: string) =>
set((state) => ({
Todos: state.Todos.map((todo) =>
todo.id === id ? { ...todo, text: newText } : todo
),
})),
updateChecked: (id: number, check: boolean) =>
set((state) => ({
Todos: state.Todos.map((todo) =>
todo.id === id ? { ...todo, isChecked: check } : todo
),
})),
}));
page.tsx
"use client";
import { useStore } from "./store/store";
import { useState } from "react";
import {
MdOutlineCheckBoxOutlineBlank,
MdOutlineCheckBox,
MdRocketLaunch,
MdCheck,
MdDelete,
MdEdit,
} from "react-icons/md";
export default function Home() {
const [todo, setTodo] = useState("");
const { Todos, addTodo, remove, updateDisabled, update, updateChecked } =
useStore((state) => state);
const handleAdd = () => {
if (!todo) return alert("Please enter a todo");
addTodo(todo);
setTodo("");
};
return (
<main>
<div className="w-full bg-indigo-50 text-center min-h-screen">
<h1 className="text-5xl font-bold pt-10 pb-5">Hello Todo App!</h1>
<div className="p-10 flex items-center">
<input
className="w-5/6 placeholder:text-gray-400 border-0 p-5 rounded-l-lg focus:outline-none"
placeholder="your todo here"
value={todo}
onChange={(e) => setTodo(e.target.value)}
/>
<button
className="w-1/6 bg-indigo-200 rounded-r-lg p-5"
onClick={handleAdd}
>
<div className="flex items-center justify-center">
<MdRocketLaunch size="24" />
</div>
</button>
</div>
<div className="px-10">
{Todos.map((todo, index) => (
<div className="flex pt-5" key={todo.id}>
<div className="flex-none mr-2 content-center">
{todo.isChecked ? (
<button onClick={() => updateChecked(todo.id, false)}>
<MdOutlineCheckBox size="30" />
</button>
) : (
<button onClick={() => updateChecked(todo.id, true)}>
<MdOutlineCheckBoxOutlineBlank size="30" />
</button>
)}
</div>
<input
className="flex-1 placeholder:text-gray-400 border-0 p-5 rounded-lg focus:outline-none w-16 md:w-32 lg:w-48"
disabled={todo.isDisabled}
value={todo.text}
onChange={(e) => update(todo.id, e.target.value)}
/>
{todo.isDisabled ? (
<button
className="flex-none mx-4 bg-indigo-200 p-5 rounded-lg"
onClick={() => updateDisabled(todo.id, false)}
>
<div className="flex items-center justify-center">
<MdEdit size="30" />
</div>
</button>
) : (
<button
className="flex-none mx-4 bg-indigo-200 p-5 rounded-lg"
onClick={() => updateDisabled(todo.id, true)}
>
<div className="flex items-center justify-center">
<MdCheck size="30" />
</div>
</button>
)}
<button
className="flex-none bg-indigo-200 p-5 rounded-lg"
onClick={() => remove(todo.id)}
>
<div className="flex items-center justify-center">
<MdDelete size="30" />
</div>
</button>
</div>
))}
</div>
</div>
</main>
);
}
๊ฐ๋จํ๊ฒ Zustand์ ์ฌ์ฉ๋ฒ์ ์ตํ๊ธฐ ์ํด todo App์ ๋ง๋ค์ด๋ดค๋๋ฐ,
(๋๋ฌด ์์ ํ ์ดํ๋ก์ ํธ๋ผ์ ์์ง ์ ์๋๊ฑด ์๋์ง๋ง........)
ํ์คํ ๋ค๋ฅธ ์ํ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ณด๋ค ์ฌ์ฉํ๋ ๋ฒ๋ ๊ฐ๋จํ๊ณ ํ์์ ์ข ๋ค๋ฃจ๋ ์ฌ๋์ด๋ผ๋ฉด ์ฝ๊ฒ ๋ฐฐ์ธ ์ ์์ ๊ฒ ๊ฐ๋ค.
์์ผ๋ก Zustand๋ฅผ ์ฌ์ฉํ ์ฌ๋ฌ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ด์ผ๊ฒ ๋ค.
์ฐธ๊ณ
Zustand Documentation (pmnd.rs)
Zustand Documentation
Zustand is a small, fast and scalable bearbones state-management solution, it has a comfy api based on hooks
docs.pmnd.rs
[React] Zustand ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ํ ๊ด๋ฆฌํ๊ธฐ (JS/TS) (velog.io)
[React] Zustand ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ํ ๊ด๋ฆฌํ๊ธฐ (JS/TS)
๋ฆฌ์กํธ์์ zustand ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๊ฒ ์ํ๊ด๋ฆฌํ๋ ๋ฒ์ ์์๋ณด์!
velog.io