|
| 1 | +# Zustand 5.0.5 |
| 2 | + |
| 3 | +- 작고 빠르며 확장 가능한 React 프로젝트에서 사용하는 상태 관리(Store) 라이브러리이다. |
| 4 | + |
| 5 | +## v5 |
| 6 | + |
| 7 | +- React v18 이상 필요 |
| 8 | +- TypeScript v4.5 이상 필요 |
| 9 | +- UMD/SystemJS 지원 중단 |
| 10 | +- ES5 지원 중단 |
| 11 | +- 기본 내보내기(Default Export) 삭제 |
| 12 | +- Persist 미들웨어가 초기 상태를 바로 스토리지에 저장하지 않음 |
| 13 | +- use-sync-external-store 패키지가 피어 종속성으로 변경, 필요한 경우 직접 설치 |
| 14 | +- setState의 상태 대체(replace)가 더욱 엄격하게 동작 |
| 15 | + |
| 16 | +## Store |
| 17 | + |
| 18 | +- 애플리케이션의 여러 상태(State, 관리하는 데이터)를 중앙에서 관리하는 패턴 |
| 19 | + 이를 통해 컴포넌트 간 데이터를 쉽게 공유하고 데이터 변경을 감지해 자동으로 렌더링(반응성) 할 수도 있다. |
| 20 | + |
| 21 | + |
| 22 | + |
| 23 | +- 만약 컴포넌트 간에 공유해야 하는 데이터가 있다면 기본적으로 부모와 자식 컴포넌트 간 데이터 전달이 가능하다. |
| 24 | +- Props 방식 |
| 25 | +- 중첩된 컴포넌트 구조에서 불필요하게 데이터를 취급하는 중간 단계의 컴포넌트가 생기고 결합도가 높아지며 유지 보수가 어려워진다. |
| 26 | +  |
| 27 | + |
| 28 | +## Props Drilling |
| 29 | + |
| 30 | +- 이를 피하기 위해 Store 를 사용해서 컴포넌트 간 공유할 데이터를 중앙에서 관리하는데 |
| 31 | + 중간 단계 컴포넌트가 필요치 않으므로 컴포넌트 간 결합도를 낮추고 유지/보수를 쉽게 만든다. |
| 32 | + |
| 33 | + |
| 34 | + |
| 35 | +```bash |
| 36 | +npm i zustand |
| 37 | +``` |
| 38 | + |
| 39 | +```tsx |
| 40 | + |
| 41 | +import { create } from 'zustand'; |
| 42 | +{/*store 생성 |
| 43 | +콜백은 set, get 매개변수를 가지고 이를 통해 상태 변경 및 조회가 가능하다. |
| 44 | +create 함수의 콜백이 반환하는 객체에서의 속성은 state 이고 메소드는 action이다. |
| 45 | +create 함수 호출에서 반환하는 스토어 훅(Hook)은 useCountStore와 같이 use접미사, Store 접미사로 명명해서 각 컴포넌트에서 사용 가능하다. |
| 46 | +*/} |
| 47 | +export const use__Store = create((set, get) => { |
| 48 | + return { |
| 49 | + 상태: initValue, |
| 50 | + action: function () => { |
| 51 | + const state = get() |
| 52 | + const {상태} = state |
| 53 | + set({ |
| 54 | + 상태 : 상태 + 1 |
| 55 | + }) |
| 56 | + } |
| 57 | + } |
| 58 | +}) |
| 59 | +``` |
| 60 | + |
| 61 | +```tsx |
| 62 | +import { create } from "zustand"; |
| 63 | + |
| 64 | +export const use이름Store = create((set) => { |
| 65 | + return { |
| 66 | + 상태: initValue, |
| 67 | + action: () => { |
| 68 | + set((state) => ({ |
| 69 | + 상태: state.상태 + 1, |
| 70 | + })); |
| 71 | + }, |
| 72 | + }; |
| 73 | +}); |
| 74 | +``` |
| 75 | + |
| 76 | +- get함수를 호출하면 상태와 액션을 가진 store 객체(state)를 얻을 수 있다. |
| 77 | +- set함수를 호출(변경할 상태를 속성으로 포함한 객체를 전달)하면 상태를 변경할 수 있다. |
| 78 | +- set함수를 호출할 때 콜백을 사용하면 get함수를 사용하지 않아도 바로 store 객체 얻을 수 있다. |
| 79 | +- 변경할 상태를 속성으로 포함한 객체를 콜백에서 반환해야 한다. |
| 80 | + |
| 81 | +```tsx |
| 82 | +import { create } from "zustand"; |
| 83 | + |
| 84 | +export const useCountStore = create<{ |
| 85 | + count: number; |
| 86 | + increase: () => void; |
| 87 | + decrease: () => void; |
| 88 | +}>((set) => ({ |
| 89 | + count: 1, |
| 90 | + increase: () => set((state) => ({ count: state.count + 1 })), |
| 91 | + decrease: () => set((state) => ({ count: state.count - 1 })), |
| 92 | +})); |
| 93 | +``` |
| 94 | + |
| 95 | +생성한 스토어를 다음과 같이 컴포넌트에서 사용할 수 있다. |
| 96 | + |
| 97 | +```tsx |
| 98 | +import { useCountStore } from "./store/count"; |
| 99 | + |
| 100 | +export default function App() { |
| 101 | + const count = useCountStore((state) => state.count); |
| 102 | + const increase = useCountStore((state) => state.increase); |
| 103 | + const decrease = useCountStore((state) => state.decrease); |
| 104 | + return ( |
| 105 | + <> |
| 106 | + <h2>{count}</h2> |
| 107 | + <button onClick={increase}>+1</button> |
| 108 | + <button onClick={decrease}>-1</button> |
| 109 | + </> |
| 110 | + ); |
| 111 | +} |
| 112 | +``` |
| 113 | + |
| 114 | +## 다중 상태 선택 (useShallow) |
| 115 | + |
| 116 | +- useShallow 훅을 사용하면 여러 상태(액션)을 한번에 객체나 배열로 가져올 수 있다. |
| 117 | +- store hook에서 Zustand의 useShallow 훅 중첩 호출해 선택자 함수를 전달한다. |
| 118 | +- 이때 선택자 함수는 사용하고자 하는 상태(액션)을 포함하는 객체나 배열을 반환해야 한다. |
| 119 | + |
| 120 | +```ts |
| 121 | +import { useShallow } from "zustand/shallow"; |
| 122 | + |
| 123 | +const { 상태, 액션 } = use이름Store( |
| 124 | + useShallow((state) => ({ |
| 125 | + 상태: state.상태, |
| 126 | + 액션: state.액션, |
| 127 | + })) |
| 128 | + // 또는 배열로 한꺼번에 가져온다. |
| 129 | + // useShallow(state => [state.상태, state.액션]) |
| 130 | +); |
| 131 | +``` |
| 132 | + |
| 133 | +- `count`, `increase`, `decrease` 상태(액션)을 하나씩 가져오지 않고 한꺼번에 가져온다. |
| 134 | +- 객체 구조 분해 할당으로 사용할 상태만 가져올 수도 있고 한번에 객체로 모아 처리하는 경우도 있다. |
| 135 | + |
| 136 | +```tsx |
| 137 | +import { useShallow } from "zustand/shallow"; |
| 138 | +import { useCountStore } from "./store/count"; |
| 139 | + |
| 140 | +export default function App() { |
| 141 | + // const count = useCountStore(state => state.count) |
| 142 | + // const increase = useCountStore(state => state.increase) |
| 143 | + // const decrease = useCountStore(state => state.decrease) |
| 144 | + |
| 145 | + // const { count, increase, decrease } = useCountStore(state => ({ |
| 146 | + const countState = useCountStore( |
| 147 | + useShallow((state) => ({ |
| 148 | + count: state.count, |
| 149 | + increase: state.increase, |
| 150 | + decrease: state.decrease, |
| 151 | + })) |
| 152 | + // 또는 한번에 배열로 가져온다. |
| 153 | + // const countState = useCountStore( |
| 154 | + //useShallow(state => [state.count, state.increase, state.decrease])) |
| 155 | + ); |
| 156 | + return ( |
| 157 | + <> |
| 158 | + <h2>{countState.count}</h2> |
| 159 | + <button onClick={countState.increase}>+1</button> |
| 160 | + <button onClick={countState.decrease}>-1</button> |
| 161 | + </> |
| 162 | + ); |
| 163 | +} |
| 164 | +``` |
| 165 | + |
| 166 | +## 액션 분리 |
| 167 | + |
| 168 | +- 여러 컴포넌트에서 단일 스토어의 액션을 많이 사용한다면 액션을 분리해 관리하는 패턴을 활용한다. |
| 169 | +- actions 객체 안에서 모든 액션을 관리하면 각 컴포넌트에서 필요한 액션만 가져오기 쉽다. |
| 170 | + |
| 171 | +```ts |
| 172 | +import { create } from "zustand"; |
| 173 | + |
| 174 | +export const useCountStore = create<{ |
| 175 | + count: number; |
| 176 | + actions: { |
| 177 | + increase: () => void; |
| 178 | + decrease: () => void; |
| 179 | + }; |
| 180 | +}>((set) => ({ |
| 181 | + count: 1, |
| 182 | + actions: { |
| 183 | + increase: () => set((state) => ({ count: state.count + 1 })), |
| 184 | + decrease: () => set((state) => ({ count: state.count - 1 })), |
| 185 | + }, |
| 186 | +})); |
| 187 | +``` |
| 188 | + |
| 189 | +```tsx |
| 190 | +import { useCountStore } from "./store/count"; |
| 191 | + |
| 192 | +export default function App() { |
| 193 | + const count = useCountStore((state) => state.count); |
| 194 | + const { increase, decrease } = useCountStore((state) => state.actions); |
| 195 | + return ( |
| 196 | + <> |
| 197 | + <h2>{count}</h2> |
| 198 | + <button onClick={increase}>+1</button> |
| 199 | + <button onClick={decrease}>-1</button> |
| 200 | + </> |
| 201 | + ); |
| 202 | +} |
| 203 | +``` |
| 204 | + |
| 205 | +[도움](https://www.heropy.dev/p/n74Tgc) |
0 commit comments