반응형
반응형

https://zustand-demo.pmnd.rs/

 

Zustand

 

zustand-demo.pmnd.rs

 

 

Zustand란

 

Zustand는 React용 여러 상태 관리 라이브러리 중 하나입니다.

 

 

Redux vs Zustand

  • Provider 미사용 및 코드 간결화

Zustand

import { create } from 'zustand'

type State = {
  count: number
}

type Actions = {
  increment: (qty: number) => void
  decrement: (qty: number) => void
}

const useCountStore = create<State & Actions>((set) => ({
  count: 0,
  increment: (qty: number) => set((state) => ({ count: state.count + qty })),
  decrement: (qty: number) => set((state) => ({ count: state.count - qty })),
}))
import { create } from 'zustand'

type State = {
  count: number
}

type Actions = {
  increment: (qty: number) => void
  decrement: (qty: number) => void
}

type Action = {
  type: keyof Actions
  qty: number
}

const countReducer = (state: State, action: Action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + action.qty }
    case 'decrement':
      return { count: state.count - action.qty }
    default:
      return state
  }
}

const useCountStore = create<State & Actions>((set) => ({
  count: 0,
  dispatch: (action: Action) => set((state) => countReducer(state, action)),
}))
const Page = () => {
  const count = useCountStore((state) => state.count)
  const increment = useCountStore((state) => state.increment)

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => increment(1)}>+</button>
    </div>
  )
}

 

 

Redux

import { createStore } from 'redux'
import { useSelector, useDispatch } from 'react-redux'

type State = {
  count: number
}

type Action = {
  type: 'increment' | 'decrement'
  qty: number
}

const countReducer = (state: State, action: Action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + action.qty }
    case 'decrement':
      return { count: state.count - action.qty }
    default:
      return state
  }
}

const countStore = createStore(countReducer)
import { createSlice, configureStore } from '@reduxjs/toolkit'

const countSlice = createSlice({
  name: 'count',
  initialState: { value: 0 },
  reducers: {
    incremented: (state, qty: number) => {
      // Redux Toolkit does not mutate the state, it uses the Immer library
      // behind scenes, allowing us to have something called "draft state".
      state.value += qty
    },
    decremented: (state, qty: number) => {
      state.value -= qty
    },
  },
})

const countStore = configureStore({ reducer: countSlice.reducer })
import { Provider } from 'react-redux'
import { countStore } from './store'

const App = () => (
	...
	
  <Provider store={countStore}>
    <MyComponent />
  </Provider>
)

const Page = () => {

  const count = useAppSelector((state) => state.count.value)
  const dispatch = useAppDispatch()
  
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch(incremented(1))}>+</button>
    </div>
  )
}

Redux는 앱이 컨텍스트 제공자로 래핑되어야 하지만, Zustand는 그렇지 않습니다.

그리고 한 눈에 보기에도 더욱 간결한 코드로 인해 사용이 편해집니다.

 

  • 렌더링 최적화 (Redux 대비)
항목 Zustand Redux
리렌더링 단위 useStore(state => state.xxx) 단위 (세밀) useSelector(state => ...) 단위 (얕은 비교)
잘못 쓰면? 적게 발생 (부분 구독 강제됨) 전체 리렌더 발생 가능 (전체 state 구독하면)
구조상 유리함 함수 단위 구독으로 명확함 객체 구독 시 실수 여지 있음

 

 

  • zustand npm download수가 더 많다

https://npm-stat.com/charts.html?package=zustand&package=jotai&package=valtio&package=%40reduxjs%2Ftoolkit&package=recoil

 

npm-stat: download statistics for NPM packages

Download statistics To keep this site running and ad-free, I would appreciate a donation. All data comes directly from npm. The charts of this service are powered by Highcharts JS which is provided under a CC BY-NC 3.0 licence. npm-stat.com is not affiliat

npm-stat.com

 

 

여담으로 공식 내용은 아니지만 Redux Toolkit은 RTK가 있기 때문에 React Query보다는 RTK가 적합하고 Zustand는 딱 상태관리만 해주기 때문에 React Query에 어울린다는 말이 있네요

 

 

출처

반응형

'[State Management]' 카테고리의 다른 글

[Redux Toolkit] 리덕스 툴킷 설치 및 사용법  (0) 2024.01.27
반응형

redux toolkit 설치

npm install @reduxjs/toolkit react-redux

 

 

store.ts

import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch

리덕스 객체 생성 및 슬라이스 등록

 

 

index.tsx

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { store } from './app/store'
import { Provider } from 'react-redux'

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

서드파트에서 사용할 수 있게 Proivder 제공

 

 

counter/counter-slice.ts

import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'

export interface CounterState {
  value: number
}

const initialState: CounterState = {
  value: 0,
}

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the Immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload
    },
  },
})

// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer

전역값 저장을 위한 Reducer 생성

 

counter.tsx

import React from 'react'
import type { RootState } from '../../app/store'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'

export function Counter() {
  const count = useSelector((state: RootState) => state.counter.value)
  const dispatch = useAppDispatch();

  return (
    <div>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(counterSlice.actions.decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  )
}

useSelector를 통해 전역으로 관리되는 상태값 가져오기

dispatch를 통해 slice에 선언한 reducer 호출로 전역 상태값 저장하기

 

 

Redux Toolkit 사용 단계

  1. Redux Proivder
  2. Redux 객체 생성
  3. Slice 생성 (Reducer 전역값 저장)
  4. Slice Redux 객체에서 사용할 수 있게 등록
  5. tsx에서 useSelector나 Dispatch로 값을 가져오거나 전역값 저장
반응형

'[State Management]' 카테고리의 다른 글

[State Management] Zustand란?, Redux vs Zustand  (0) 2025.05.20