본문 바로가기
🖥️ Frontend/frontend+

🐻 Zustand

by 정람지 2024. 8. 19.
 

Introduction - Zustand

How to use Zustand

zustand.docs.pmnd.rs

주스탠드 (Zustand)

주스탠드는 상태 관리 라이브러리로, Redux와 같은 기존의 상태 관리 라이브러리보다 간단하고 직관적인 API를 제공

리덕스의 복잡한 설정과 보일러플레이트 없이도 상태를 효과적으로 관리

 

주요 기능:

  1. 간단한 설정: 작은 설정으로 강력한 상태 관리가 가능합니다.
  2. 훅 기반 API: useStore 같은 훅을 사용해서 컴포넌트 내에서 직접 상태를 접근하고 업데이트할 수 있습니다.
  3. 비동기 로직 통합: 액션 내에서 비동기 로직을 쉽게 통합하고 관리할 수 있습니다.
  4. 컴포넌트 간 상태 공유: 애플리케이션의 다양한 컴포넌트 간에 상태를 쉽게 공유하고 업데이트할 수 있습니다.

🐻 예제

import { create } from 'zustand'

const useStore = create((set) => ({
  count: 1,
  inc: () => set((state) => ({ count: state.count + 1 })),
}))

function Counter() {
  const { count, inc } = useStore()
  return (
    <div>
      <span>{count}</span>
      <button onClick={inc}>one up</button>
    </div>
  )
}

useStore라는 이름의 상태 훅을 생성

count: 1: 초기 상태로 count 변수를 1로 설정

inc: () => set((state) => ({ count: state.count + 1 })): inc라는 함수를 정의합니다. 이 함수는 현재 상태(state)를 가져와서 count 값을 1 증가시킨 새로운 상태를 설정


🐻 introduction

  store 만들기 

 

import { create } from 'zustand'

create 가져오기

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

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> }

primitives, objects, functions 들어감

The set function merges state 

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> }

훅 사용하기 

 


🐻 Updating state

Call the provided set function with the new state, and it will be shallowly merged with the existing state in the store

import { create } from 'zustand'

type State = {
  firstName: string
  lastName: string
}

type Action = {
  updateFirstName: (firstName: State['firstName']) => void
  updateLastName: (lastName: State['lastName']) => void
}

// Create your store, which includes both state and (optionally) actions
const usePersonStore = create<State & Action>((set) => ({
  firstName: '',
  lastName: '',
  updateFirstName: (firstName) => set(() => ({ firstName: firstName })),
  updateLastName: (lastName) => set(() => ({ lastName: lastName })),
}))

// In consuming app
function App() {
  // "select" the needed state and actions, in this case, the firstName value
  // and the action updateFirstName
  const firstName = usePersonStore((state) => state.firstName)
  const updateFirstName = usePersonStore((state) => state.updateFirstName)

  return (
    <main>
      <label>
        First name
        <input
          // Update the "firstName" state
          onChange={(e) => updateFirstName(e.currentTarget.value)}
          value={firstName}
        />
      </label>

      <p>
        Hello, <strong>{firstName}!</strong>
      </p>
    </main>
  )
}

usePersonStore

 Zustand 스토어를 생성하며, firstName lastName 상태를 초기화하고, 각각을 업데이트하는 함수를 포함

 

App 컴포넌트

스토어를 사용하여 firstName 상태를 렌더링하고, 입력 필드에서의 변경을 updateFirstName을 통해 상태에 반영

 

 

+ 깊이 중첩된 걕체 상태 다루기

Immer를 사용한 접근 방식

immerInc: () =>
  set(produce((state: State) => { ++state.deep.nested.obj.count })),

Immer의 produce 함수는 현재 상태를 첫 번째 인자로 받고, 두 번째 인자로 전달된 함수 내에서 상태를 직접 수정하면서 새로운 상태를 불변하게 반환

optics-ts, Ramda도 사용 가능


🐻 Immutable state and merging

불변 상태의 중요성

Zustand는 불변성을 유지하면서 상태를 관리할 것을 권장

불변성을 유지한다는 것은 상태 객체를 직접 수정하지 않고, 상태 변경이 필요할 때마다 새로운 객체를 생성하여 반환함을 의미

이는 여러 컴포넌트 간에 상태가 공유될 때 부작용을 방지하고, 상태 변경을 예측 가능하게 만들어 주며, 성능 최적화를 위한 React의 리렌더링 최적화와도 잘 호환

 

상태 병합 방법

Zustand에서 상태를 업데이트할 때는 set 함수를 사용

이 함수는 새로운 상태 객체를 입력받아 기존의 상태와 얕게(shallow) 병합

얕은 병합은 상태 객체의 첫 번째 레벨만을 병합하는 것을 의미

 

중첩된 객체를 다룰 때는 각 레벨을 명시적으로 복사하고 업데이트해야 함!

 

import { create } from 'zustand'

const useCountStore = create((set) => ({
  count: 0,
  inc: () => set((state) => ({ count: state.count + 1 })),
}))

라이브러리와 도구

상태의 불변성을 보다 쉽게 관리하기 위해 Immer 같은 라이브러리의 사용이 권장될 수 있음


🐻 Flux inspired practice

Zustand에서 Flux와 유사한 방식으로 상태 관리를 구현하는 방법에 대해 설명

Flux는 Facebook에서 제안한 애플리케이션 아키텍처로, 애플리케이션의 데이터 흐름을 단방향으로 유지하며, 상태 관리의 복잡성을 줄이는 것을 목표로 함

 

주요 개념

  1. 단방향 데이터 흐름:
    • Flux의 핵심 개념 중 하나는 단방향 데이터 흐름입니다. 이는 상태의 변경이 항상 예측 가능하게 일어날 수 있도록, 액션이 상태를 변경하고 그 결과가 UI에 반영되는 순서를 명확히 하는 것입니다.
    • Zustand에서는 set 함수를 통해 상태를 변경하고, 이 변경된 상태는 자동으로 구독된 컴포넌트에 반영됩니다.
  2. 액션 디스패칭:
    • Flux에서는 상태를 변경하기 위해 디스패치(dispatch)라는 개념을 사용하여 액션을 전달합니다. Zustand에서도 비슷한 방식으로, 상태를 업데이트하는 함수(액션)를 정의하고 이를 호출하여 상태를 변경할 수 있습니다.
  3. 중앙 집중식 스토어:
    • Zustand는 단일 스토어를 사용하여 모든 애플리케이션 상태를 관리할 수 있습니다. 이는 Flux의 Store와 유사하게 작동합니다.
    • 상태 업데이트 로직과 상태를 중앙에서 관리하여, 애플리케이션의 데이터 흐름을 쉽게 추적하고 관리할 수 있습니다.
  4. 불변성 유지:
    • 상태 변경 시 직접 상태를 수정하는 대신, 새로운 상태 객체를 생성하여 기존 상태와 병합하는 방식으로 상태를 업데이트합니다. 이는 상태 관리의 일관성을 유지하고, 예측 가능한 동작을 보장하는 데 도움을 줍니다.

그 밑에 카테고리는...

읽어도 잘 이해가 안 된다..

일단 써보고 다시 공부해볼까


추억여행

 

'🖥️ Frontend > frontend+' 카테고리의 다른 글

⛈️vite🪁 알아보기  (3) 2024.09.13
🌺 React-query 공부하기  (0) 2024.09.02
🌸 React Query & Zustand 🌸  (2) 2024.08.19
프론트엔드 : 💝Emotion  (0) 2024.08.08
🌏 RealWorld 프로젝트 - react/recoil  (0) 2024.04.02