어제 저녁까지 공부한 내용인데, zustand가 사용성이 굉장히 좋은 것 같다.
recoil아니면 zustand가 사용성도 좋고, react 문법과 구조도 비슷해서 편리한 것 같다.
솔직히 근본은 redux라고 하던데, redux는 너무 복잡하게 느껴진다.
아무튼, 이번글에서는 zustand에서 다양한 기능들을 알아볼 것이다.
지난번글에서는 zustand를 이용해서 store를 만들고, 저장하고, 가져와서 사용했었는데,
실제로는 더 다양한 기능들이 있겠지만, 기본적인 기능들을 알아보도록하자.
1. 스토리지저장
스토리지저장은 persist라는 메소드를 사용하면 된다.
persist는 zustand/middleware 안에 있고, zustand 설치했으면 그냥 사용할 수 있다.
- 사용법은 zustand로 만든 store전체를 감싸주는게 방법이다.
- 그리고, 두번째 매개변수를 꼭 넣어줘야한다. 언제부터 바뀐지는 모르겠지만, 현재 버전(4.5.1)에서는 안되더라
- 두번째 매개변수에 들어갈 수 있는 것들은 다양한 것들이 있는데, 기본적으로 name이라는 속성을 넣어줘야 storage에 들어갈 때 이름이 생긴다. 안넣으면 undefined로 나온다.
- 다른 속성도 넣으면 저장되는 스토리지를 정할 수 있는 등등 기능이 추가된다.
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
let todoStore = (set) => ({
todos: [],
addTodo: (todoText) =>
set(state => ({
todos: [
...state.todos,
{
text: todoText,
id: getId(),
isCompleted: false
}
]
})),
deleteTodo: (todoId) =>
set(state => ({
todos: state.todos.filter((todo) => todo.id !== todoId)
})),
completeTodo: (todoId) =>
set(state => ({
todos: state.todos.map((todo) => {
if(todo.id === todoId) {
return {
...todo,
isCompleted: true
}
}
return todo;
})
}))
})
todoStore = devtools(todoStore);
todoStore = persist(todoStore, {name: 'todo'});
export const useTodoStore = create(todoStore);
let id = 0;
function getId() {
return id++;
}
2. 스토리지 삭제
스토리지에 저장했으면 지울수도 있어야한다.
지우는 방법은 아래와 같다.
- 스토리지이름.persist.clearStorage(); 하면 스토리지에 저장된 데이터가 다 날라간다.
import React from 'react'
import { useConterStore } from '../store/useCounterStore';
const Counter = () => {
const { count, increment, reset, setNumber } = useConterStore();
const clear = () => {
useConterStore.persist.clearStorage();
// store이름 하나 잡고 persist.clearStorage() 하면 storage 다 날라감, value만 사라지는게 아니라 그냥 다 사라짐
}
return (
<div>
<p>{count}</p>
<button onClick={increment}>one up</button>
<button onClick={reset}>reset</button>
<button onClick={() => setNumber(3)}>set number</button>
<button onClick={clear}>clear</button>
</div>
)
}
export default Counter
3. redux devtools 사용하기
zustand는 redux devtools 를 사용할 수 있다.
redux 쓰는 이유가 사실 devtools 쓰려고 하는거라고 개인적으로 생각하는데,
이거 쓸수 있으니까 어려운 redux안써도 될 듯
devtools도 zustand/middleware안에 있다.
사용법은 아래와 같다.
- devtools 메소드에 store변수를 넣어주면 끝이다.
import { create } from "zustand";
import { devtools, persist } from 'zustand/middleware'
let counterStore = (set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
reset: () => set({count: 0}),
setNumber: (number) => set({count: number})
});
counterStore = devtools(counterStore);
counterStore = persist(counterStore, {name: 'counter'});
export const useConterStore = create(counterStore);
4. 비동기 처리하기
비동기 처리하는 방법은 앞에서 정리한 내용들을 기억한다면 별거없다.
react 기본 내장 fetch를 사용했다.
async, await 이용해서 fetch함수를 store안에 만들었다.
import { create } from "zustand";
export const useUserStore = create((set) => ({
user: {},
fetch: async (id) => {
const response = await fetch(path);
set({user: await response.json()});
}
}));
구조분해할당으로 store가져오고, fetch는 useEffect안에 콜백으로 넣었다.
그러면 store안에 user에 id: 1에 대한 유저데이터 전체가 들어갔고, 객체형태라서 그냥 쓸 수 있다.
import { useEffect } from 'react';
import './App.css';
import Counter from './components/Counter';
import TodoList from './components/TodoList';
import { useUserStore } from './store/useUserStore';
function App() {
const {fetch, user} = useUserStore();
console.log(user);
useEffect(() => {
fetch(1);
},[fetch]);
return (
<div className="App">
<header className='App-header'>
<Counter />
<TodoList />
<p>
{user.name}
</p>
<p>
{user.phone}
</p>
</header>
</div>
);
}
export default App;
전체 만들어진거 코드 구경하기
- 이전 글에 있던 repository에 새로운 버전으로 만들어뒀다.
https://github.com/jmpark24/react-zustand-app
GitHub - jmpark24/react-zustand-app
Contribute to jmpark24/react-zustand-app development by creating an account on GitHub.
github.com
이렇게 생겼다. 기능들도 잘 돌아간다.