@@ -64,93 +64,216 @@ pnpm add @context-query/react
6464### 1. Context Query Provider 생성
6565
6666``` tsx
67- // CounterContextQueryProvider .tsx
67+ // UserContextQueryProvider .tsx
6868import { createContextQuery } from " @context-query/react" ;
6969
70+ interface UserData {
71+ name: string ;
72+ email: string ;
73+ preferences: {
74+ theme: " light" | " dark" ;
75+ notifications: boolean ;
76+ };
77+ }
78+
7079export const {
71- Provider : CounterQueryProvider,
72- useContextQuery : useCounterQuery,
73- } = createContextQuery <{ count1: number ; count2: number }>();
80+ Provider : UserQueryProvider,
81+ useContextQuery : useUserQuery,
82+ updateState : updateUserState,
83+ setState : setUserState,
84+ } = createContextQuery <UserData >({
85+ name: " " ,
86+ email: " " ,
87+ preferences: {
88+ theme: " light" ,
89+ notifications: true ,
90+ },
91+ });
7492```
7593
76- ### 2. Provider로 컴포넌트 트리 감싸기
94+ ### 2. Provider로 컴포넌트 트리 감싸기 및 상태 초기화
7795
7896``` tsx
79- // ParentComponent.tsx
80- import { CounterQueryProvider } from " ./CounterContextQueryProvider" ;
97+ // UserProfilePage.tsx
98+ import { UserQueryProvider , updateUserState } from " ./UserContextQueryProvider" ;
99+
100+ async function fetchUserData(userId : string ) {
101+ const response = await fetch (` /api/users/${userId } ` );
102+ return response .json ();
103+ }
104+
105+ function UserProfilePage({ userId }: { userId: string }) {
106+ useEffect (() => {
107+ // 외부 데이터로 상태 초기화
108+ const loadUserData = async () => {
109+ const userData = await fetchUserData (userId );
110+ updateUserState (userData ); // 가져온 데이터로 전체 상태 업데이트
111+ };
112+ loadUserData ();
113+ }, [userId ]);
81114
82- function ParentComponent() {
83115 return (
84- <CounterQueryProvider initialState = { count1 : 0 , count2 : 0 } >
85- <ChildComponentA /> { /* count1이 변경될 때만 리렌더링 */ }
86- <ChildComponentB /> { /* count2가 변경될 때만 리렌더링 */ }
87- </CounterQueryProvider >
116+ <UserQueryProvider >
117+ <div className = " user-profile" >
118+ <UserInfoForm />
119+ <UserPreferencesForm />
120+ <SaveButton />
121+ </div >
122+ </UserQueryProvider >
88123 );
89124}
90125```
91126
92127### 3. 컴포넌트에서 상태 사용하기
93128
94129``` tsx
95- // ChildComponentA.tsx
96- import { useCounterQuery } from " ./CounterContextQueryProvider" ;
97-
98- function ChildComponentA() {
99- const [count, setCount] = useCounterQuery (" count1" );
130+ // UserInfoForm.tsx
131+ import { useUserQuery } from " ./UserContextQueryProvider" ;
100132
101- console .log (" ChildComponentA 렌더링" ); // count1이 변경될 때만 로그가 출력됩니다
102-
103- const increment = () => {
104- setCount (count + 1 );
105- };
133+ function UserInfoForm() {
134+ // 사용자 정보 필드만 구독
135+ const [state, setState] = useUserQuery ([" name" , " email" ]);
106136
107137 return (
108- <div >
109- <h2 >카운터 A: { count } </h2 >
110- <button onClick = { increment } >+</button >
138+ <div className = " user-info" >
139+ <h3 >기본 정보</h3 >
140+ <div >
141+ <label >이름:</label >
142+ <input
143+ value = { state .name }
144+ onChange = { (e ) =>
145+ setState ((prev ) => ({ ... prev , name: e .target .value }))
146+ }
147+ />
148+ </div >
149+ <div >
150+ <label >이메일:</label >
151+ <input
152+ value = { state .email }
153+ onChange = { (e ) =>
154+ setState ((prev ) => ({ ... prev , email: e .target .value }))
155+ }
156+ />
157+ </div >
111158 </div >
112159 );
113160}
114161
115- // ChildComponentB.tsx
116- import { useCounterQuery } from " ./CounterContextQueryProvider" ;
117-
118- function ChildComponentB() {
119- const [count, setCount] = useCounterQuery (" count2" );
120-
121- console .log (" ChildComponentB 렌더링" ); // count1이 변경될 때는 로그가 출력되지 않고, count2가 변경될 때만 출력됩니다
162+ // UserPreferencesForm.tsx
163+ import { useUserQuery } from " ./UserContextQueryProvider" ;
164+
165+ function UserPreferencesForm() {
166+ // preferences만 구독
167+ const [state, setState] = useUserQuery ([" preferences" ]);
168+
169+ const toggleTheme = () => {
170+ setState ((prev ) => ({
171+ ... prev ,
172+ preferences: {
173+ ... prev .preferences ,
174+ theme: prev .preferences .theme === " light" ? " dark" : " light" ,
175+ },
176+ }));
177+ };
122178
123- const increment = () => {
124- setCount (count + 1 );
179+ const toggleNotifications = () => {
180+ setState ((prev ) => ({
181+ ... prev ,
182+ preferences: {
183+ ... prev .preferences ,
184+ notifications: ! prev .preferences .notifications ,
185+ },
186+ }));
125187 };
126188
127189 return (
128- <div >
129- <h2 >카운터 B: { count } </h2 >
130- <button onClick = { increment } >+</button >
190+ <div className = " user-preferences" >
191+ <h3 >사용자 설정</h3 >
192+ <div >
193+ <label >테마: { state .preferences .theme } </label >
194+ <button onClick = { toggleTheme } >테마 변경</button >
195+ </div >
196+ <div >
197+ <label >
198+ <input
199+ type = " checkbox"
200+ checked = { state .preferences .notifications }
201+ onChange = { toggleNotifications }
202+ />
203+ 알림 활성화
204+ </label >
205+ </div >
131206 </div >
132207 );
133208}
209+
210+ // SaveButton.tsx
211+ import { useUserQuery , updateUserState } from " ./UserContextQueryProvider" ;
212+
213+ function SaveButton() {
214+ // 저장을 위해 모든 사용자 데이터 가져오기
215+ const [userData] = useUserQuery ([" name" , " email" , " preferences" ]);
216+
217+ const handleSave = async () => {
218+ try {
219+ const response = await fetch (" /api/users/update" , {
220+ method: " POST" ,
221+ body: JSON .stringify (userData ),
222+ });
223+ const updatedUser = await response .json ();
224+
225+ // 서버 응답으로 전체 상태 업데이트
226+ updateUserState (updatedUser );
227+ } catch (error ) {
228+ console .error (" 사용자 데이터 저장 실패:" , error );
229+ }
230+ };
231+
232+ return <button onClick = { handleSave } >변경사항 저장</button >;
233+ }
134234```
135235
236+ 이 예시는 다음을 보여줍니다:
237+
238+ 1 . 사용자 정보와 환경설정을 별도의 컴포넌트로 분리하여 관심사를 분리
239+ 2 . 각 컴포넌트는 필요한 상태만 구독하여 리렌더링을 최적화
240+ 3 . 컴포넌트들이 독립적으로 자신의 관련 상태를 업데이트
241+
136242## 고급 사용법
137243
138- ### 함수 업데이트
244+ ### 함수형 업데이트
139245
140- React의 ` useState ` 와 유사하게 상태 설정자에 함수를 전달할 수 있습니다:
246+ React의 ` useState ` 와 유사하게, 상태 설정자에 함수를 전달할 수 있습니다:
141247
142248``` tsx
143- const [count, setCount] = useCounterQuery (" count1" );
249+ const [count, setCount] = useCounterQuery ([ " count1" ] );
144250
145251// 이전 상태를 기반으로 업데이트
146252const increment = () => {
147253 setCount ((prevCount ) => prevCount + 1 );
148254};
255+ // ...
149256```
150257
151- ### 다중 Provider
258+ ### 다중 상태 업데이트
152259
153- 다른 컴포넌트 서브트리를 위해 여러 Provider를 사용할 수 있습니다:
260+ updateState 함수를 사용하여 여러 상태를 한 번에 업데이트할 수 있습니다:
261+
262+ ``` tsx
263+ import { updateCounterState } from " ./CounterContextQueryProvider" ;
264+
265+ // 여러 상태를 한 번에 업데이트
266+ const resetCounters = () => {
267+ updateCounterState ({
268+ count1: 0 ,
269+ count2: 0 ,
270+ });
271+ };
272+ ```
273+
274+ ### 다중 프로바이더
275+
276+ 서로 다른 컴포넌트 트리에 대해 여러 프로바이더를 사용할 수 있습니다:
154277
155278``` tsx
156279function App() {
@@ -174,7 +297,7 @@ function App() {
174297
175298- ` @context-query/core ` : 핵심 기능 및 상태 관리
176299- ` @context-query/react ` : React 바인딩 및 훅
177- - ` playground ` : 라이브러리를 보여주는 데모 애플리케이션
300+ - ` playground ` : 라이브러리 사용 예제 애플리케이션
178301
179302## 개발
180303
@@ -195,9 +318,6 @@ pnpm install
195318
196319# 모든 패키지 빌드
197320pnpm build
198-
199- # 플레이그라운드 데모 실행
200- pnpm playground
201321```
202322
203323## 릴리즈 워크플로우
0 commit comments