Zustand.1 기본예제를 활용한 사용방법

Hunter Joe·2024년 12월 14일
1

Zustand의 핵심 기능 + 설명을 담은 글

Intro

Zustand : 작고 빠르며 확장 가능한 React 프로젝트에서 사용하는 상태 관리 라이브러리

  • Zustand는 Hook 기반의 API이다.
  • 틀에 박힌 형식이나 고집은 없지만, 충분한 규칙성을 가지고 있어서 명시적이고 Flux와 비슷한 방식이다.

Flux ?
Flux는 Facebook에서 제안한 애플리케이션 아키텍처로 React와 함께 사용하기 위해 만들어졌다. Flux 방식의 핵심은 단방향 데이터 흐름(unidirectional data flow)를 따르는것이다.
-> 즉, 애플리케이션의 상태 관리를 간단하고 예측 가능하게 만든다.

Store

zustand.docs 에 나와있는 예제 + TS(그냥 한번 해보고싶어서 내가 추가함)

  • TS를 사용할 때는, create 함수의 제네릭으로 상태(State)와 액션(Action) 타입을 전달합니다. >>> create<GENERIC>()
interface BearStore {
  // Init State
  bears : number
  
  // Actions
  increasePopulation  : () => void
  removeAllBears : () => void
  updateBears : (newBears: number) => void
}

import { create } from 'zustand'

const useStore = create<BearStore>((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
  updateBears: (newBears) => set({ bears: newBears }),
}))

create

  • create()함수로 store를 생성한다.
  • create()함수의 콜백은 set,get매개변수를 가지며, 이를 통해 상태를 변경하거나 조회할 수 있다.
  • create()함수의 콜백이 반환하는 객체에서의 속성은 State이고, 메소드는 Action이다.
  • 이렇게 만든 store는 Redux처럼 Provider를 제공하지 않아도 어디에서든지 Hook처럼 꺼내와 사용이 가능하다.
    다른 상태 관리 Lib 들과 비교
import { create } from 'zustand'
export const useBearStore = create((set, get) => {
  return {
    const bears = get()//상태: 초깃값,
    //액션: 함수
  }
})

Use case

import React from "react";
import { create } from "zustand";

export const useBearStore = create((set, get) => {
	return {
		bears: 0,
      	food : 0 // << 이거 상태 업데이트 예제로 쓸 예정
		increaseBear1: () => {
			const state = get();
			const { bears } = state;
			set({ bears: bears + 1 });
		},
	};
});

const App = () => {
	const bears = useBearStore((state) => state.bears);
	const addBear = useBearStore((state) => state.increaseBear1);
	const increaseBear = useBearStore((state) => state.increaseBear2);
	const logState = () => {
		const state = useBearStore.getState();
		console.log("Full state:", state);
	};

	return (
		<>
			<h1>{bears} around here...</h1>
			<button onClick={addBear}> + 1</button>
			<button onClick={increaseBear}> + 1</button>
			<button onClick={logState}>Log Full State</button>
		</>
	);
};

export default App;

⚠️상태 호출할 때 주의할 점⚠️

1. 콜백 없이 호출

  • 콜백 없이 store hook을 호출하면 개별 상태나 액션이 아닌 스토어 객체를 얻을 수 있지만, 이는 사용하지 않는 상태가 변경돼도 컴포넌트가 다시 렌더링 되기 때문에 권장하지 않는 방법
// Food 컴포넌트 
const Food = () => {
	const food = useBearStore((state) => state.food);
	console.log("Food rendered:", food);
	return <h1>Food: {food}</h1>;
};

// App 컴포넌트
const App = () => {
  	// 객체 분해로 꺼내옴, Food는 사용 X 하지만 Food 재렌더링 발생
	const { bears, food } = useBearStore(); 
	const increaseBear = useBearStore((state) => state.increaseBear2);

	return (
		<>
			<Food />
			<h1>{bears} bears around here...</h1>
			<button onClick={increaseBear}> + 1 Bear</button>
		</>
	);
};
export default App;
  • 저번에 사용할 때 객체 구조 분해 할당으로 가져왔었는데 잘못된 방법이였음

2. 상태 가져올 때 한번만 가져오기 (재렌더링X)

  • .getState()를 사용하면 상태에대한 구독을 하지 않는다.
    즉, store의 상태가 바뀌어도 컴포넌트는 다시 렌더링 되지 않는다.
const App = () => {
	const { bears } = useBearStore.getState();
	const increaseBear = useBearStore((state) => state.increaseBear2);

	return (
		<>
			<Food />
			<h1>{bears} bears around here...</h1>
			<button onClick={increaseBear}> + 1 Bear</button>
		</>
	);
};
export default App;

카운터 예제로 사용법 보기

get함수를 호출하면, 상태와 액션이 포함된 스토어 객체를 얻을 수 있다.
이를 통해, 각 액션에서 상태의 값을 얻을 수 있음

import { create } from 'zustand'

export const useCountStore = create<{
  count: number
  increase: () => void
  decrease: () => void
}>((set, get) => ({
  count: 1,
  increase: () => {
    const { count } = get()
    set({ count: count + 1 })
  },
  decrease: () => {
    const { count } = get()
    set({ count: count - 1 })
  }
}))

get함수를 사용하지 않고 set함수의 콜백을 사용하면 더 간결하게 상태 변경 가능

import { create } from 'zustand'

export const useCountStore = create<{
  count: number
  increase: () => void
  decrease: () => void
}>(set => ({
  count: 1,
  increase: () => set(state => ({ count: state.count + 1 })),
  decrease: () => set(state => ({ count: state.count - 1 }))
}))

액션 분리

여러 컴포넌트에서 단일 스토어의 액션을 많이 사용한다면, 액션을 분리해 관리하는 패턴을 활용할 것

import { create } from 'zustand'

export const useCountStore = create(set => ({
  count: 1,
  actions: {
    increase: () => set(state => ({ count: state.count + 1 })),
    decrease: () => set(state => ({ count: state.count - 1 }))
  }
}))

아래는 ts로 작성한 코드

import { create } from 'zustand'

export const useCountStore = create<{
  count: number
  actions: {
    increase: () => void
    decrease: () => void
  }
}>(set => ({
  count: 1,
  actions: {
    increase: () => set(state => ({ count: state.count + 1 })),
    decrease: () => set(state => ({ count: state.count - 1 }))
  }
}))

Use case

import { useCountStore } from './store/count'

export default function App() {
  const count = useCountStore(state => state.count)
  const { increase, decrease } = useCountStore(state => state.actions)
  return (
    <>
      <h2>{count}</h2>
      <button onClick={increase}>+1</button>
      <button onClick={decrease}>-1</button>
    </>
  )
}

여기까지 Zustand 기본 사용방법이다.
확실히 Zustand는 편하다.

참고자료

profile
Async FE 취업 준비중.. Await .. (취업완료 대기중) ..

0개의 댓글