@ehfuse/forma 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -59
- package/dist/esm/hooks/useFieldState.d.ts +79 -0
- package/dist/esm/hooks/useFieldState.d.ts.map +1 -0
- package/dist/esm/hooks/useFieldState.js +154 -0
- package/dist/esm/hooks/useFieldState.js.map +1 -0
- package/dist/esm/hooks/useForm.d.ts +1 -1
- package/dist/esm/hooks/useForm.d.ts.map +1 -1
- package/dist/esm/hooks/useForm.js +33 -71
- package/dist/esm/hooks/useForm.js.map +1 -1
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/hooks/useFieldState.d.ts +79 -0
- package/dist/hooks/useFieldState.d.ts.map +1 -0
- package/dist/hooks/useFieldState.js +157 -0
- package/dist/hooks/useFieldState.js.map +1 -0
- package/dist/hooks/useForm.d.ts +1 -1
- package/dist/hooks/useForm.d.ts.map +1 -1
- package/dist/hooks/useForm.js +32 -70
- package/dist/hooks/useForm.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -9,19 +9,19 @@
|
|
|
9
9
|
|
|
10
10
|
### 한국어 | Korean
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
12
|
+
- 🚀 **[시작 가이드](./docs/getting-started-ko.md)** - 단계별 튜토리얼과 예제
|
|
13
|
+
- 📖 **[완전한 문서](./docs/README-ko.md)** - API 레퍼런스, 사용 사례, 고급 기능
|
|
14
|
+
- 📋 **[API 레퍼런스](./docs/API-ko.md)** - 모든 메서드, 타입, 마이그레이션 가이드
|
|
15
|
+
- ⚡ **[성능 최적화 가이드](./docs/best-practices-ko.md)** - 최고 성능을 위한 모범 사례
|
|
16
|
+
- 🏠 **[GitHub](https://github.com/ehfuse/forma)**
|
|
17
17
|
|
|
18
18
|
### English
|
|
19
19
|
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
20
|
+
- 🚀 **[Getting Started Guide](./docs/getting-started-en.md)** - Step-by-step tutorial and examples
|
|
21
|
+
- 📖 **[Complete Documentation](./docs/README-en.md)** - API reference, use cases, advanced features
|
|
22
|
+
- 📋 **[API Reference](./docs/API-en.md)** - All methods, types, migration guide
|
|
23
|
+
- ⚡ **[Performance Optimization Guide](./docs/best-practices-en.md)** - Best practices for optimal performance
|
|
24
|
+
- 🏠 **[GitHub](https://github.com/ehfuse/forma)**
|
|
25
25
|
|
|
26
26
|
Forma는 React 애플리케이션에서 폼 상태를 효율적으로 관리하기 위한 고성능 라이브러리입니다. 개별 필드 구독을 통한 선택적 리렌더링과 글로벌 폼 상태 공유 기능을 제공합니다.
|
|
27
27
|
|
|
@@ -29,13 +29,14 @@ _Forma is a high-performance library for efficiently managing form state in Reac
|
|
|
29
29
|
|
|
30
30
|
## 🚀 주요 특징 | Key Features
|
|
31
31
|
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
32
|
+
- ✅ **개별 필드 구독** | **Individual Field Subscription**: 필드별 선택적 리렌더링으로 최적화된 성능
|
|
33
|
+
- ✅ **범용 상태 관리** | **General State Management**: `useFieldState`로 폼 외 일반 상태도 효율적 관리
|
|
34
|
+
- ✅ **Dot Notation 최적화** | **Dot Notation Optimization**: `user.profile.name` 형태의 중첩 객체 접근
|
|
35
|
+
- ✅ **MUI 완전 호환** | **Full MUI Compatibility**: Material-UI 컴포넌트와 완벽한 통합
|
|
36
|
+
- ✅ **글로벌 폼 상태** | **Global Form State**: 여러 컴포넌트 간 폼 상태 공유
|
|
37
|
+
- ✅ **폼 등록 시스템** | **Form Registration System**: 기존 폼을 글로벌로 등록 가능
|
|
38
|
+
- ✅ **TypeScript 완전 지원** | **Full TypeScript Support**: 강력한 타입 안전성
|
|
39
|
+
- ✅ **React 19 최적화** | **React 19 Optimized**: 최신 React 기능 활용
|
|
39
40
|
|
|
40
41
|
## 📦 설치 | Installation
|
|
41
42
|
|
|
@@ -49,6 +50,8 @@ yarn add @ehfuse/forma
|
|
|
49
50
|
|
|
50
51
|
## 🎯 빠른 시작 | Quick Start
|
|
51
52
|
|
|
53
|
+
### 폼 상태 관리 | Form State Management
|
|
54
|
+
|
|
52
55
|
```tsx
|
|
53
56
|
import { useForm } from "@ehfuse/forma";
|
|
54
57
|
|
|
@@ -78,19 +81,64 @@ function MyForm() {
|
|
|
78
81
|
}
|
|
79
82
|
```
|
|
80
83
|
|
|
84
|
+
### 일반 상태 관리 | General State Management
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { useFieldState } from "@ehfuse/forma";
|
|
88
|
+
|
|
89
|
+
function TodoApp() {
|
|
90
|
+
const state = useFieldState({
|
|
91
|
+
todos: [
|
|
92
|
+
{ id: 1, text: "Learn React", completed: false },
|
|
93
|
+
{ id: 2, text: "Build app", completed: false },
|
|
94
|
+
],
|
|
95
|
+
filter: "all",
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// 개별 필드 구독 - 해당 필드가 변경될 때만 리렌더링
|
|
99
|
+
const filter = state.useValue("filter");
|
|
100
|
+
|
|
101
|
+
// ✅ 배열 길이만 구독 (항목 추가/삭제 시에만 리렌더링)
|
|
102
|
+
const todosLength = state.useValue("todos.length");
|
|
103
|
+
|
|
104
|
+
// ✅ 특정 할 일의 텍스트만 구독 (dot notation 활용)
|
|
105
|
+
const firstTodoText = state.useValue("todos.0.text");
|
|
106
|
+
|
|
107
|
+
const addTodo = () => {
|
|
108
|
+
const todos = state.getValues().todos;
|
|
109
|
+
state.setValue("todos", [
|
|
110
|
+
...todos,
|
|
111
|
+
{ id: Date.now(), text: "New todo", completed: false },
|
|
112
|
+
]);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div>
|
|
117
|
+
<p>필터: {filter}</p>
|
|
118
|
+
<p>첫 번째 할 일: {firstTodoText}</p>
|
|
119
|
+
<p>총 개수: {todosLength}</p>
|
|
120
|
+
<button onClick={addTodo}>할 일 추가</button>
|
|
121
|
+
<button onClick={() => state.setValue("filter", "completed")}>
|
|
122
|
+
완료된 항목 보기
|
|
123
|
+
</button>
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
81
129
|
## � 문서 | Documentation
|
|
82
130
|
|
|
83
131
|
### 한국어 | Korean
|
|
84
132
|
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
133
|
+
- 📖 **[완전한 문서](./docs/README-ko.md)** - API 레퍼런스, 사용 사례, 고급 기능
|
|
134
|
+
- 🌐 **[useGlobalForm 사용법](./docs/useGlobalForm-guide-ko.md)** - 글로벌 폼 상태 관리 가이드
|
|
135
|
+
- ⚡ **[성능 최적화 가이드](./docs/best-practices-ko.md)** - 최고 성능을 위한 모범 사례
|
|
88
136
|
|
|
89
137
|
### English
|
|
90
138
|
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
139
|
+
- 📖 **[Complete Documentation](./docs/README-en.md)** - API reference, use cases, advanced features
|
|
140
|
+
- 🌐 **[useGlobalForm Usage Guide](./docs/useGlobalForm-guide-en.md)** - Global form state management guide
|
|
141
|
+
- ⚡ **[Performance Optimization Guide](./docs/best-practices-en.md)** - Best practices for optimal performance
|
|
94
142
|
|
|
95
143
|
## 🎯 When to choose Forma?
|
|
96
144
|
|
|
@@ -107,52 +155,52 @@ Forma는 **폼 상태 관리에 특화**된 라이브러리로 특정 시나리
|
|
|
107
155
|
**🎨 MUI (Material-UI) Projects**
|
|
108
156
|
**MUI (Material-UI) 프로젝트**
|
|
109
157
|
|
|
110
|
-
-
|
|
111
|
-
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
-
|
|
115
|
-
|
|
158
|
+
- Seamless integration with MUI components
|
|
159
|
+
MUI 컴포넌트와 완벽한 통합
|
|
160
|
+
- No additional wrapper components needed
|
|
161
|
+
추가 래퍼 컴포넌트 불필요
|
|
162
|
+
- Built-in support for MUI's controlled component patterns
|
|
163
|
+
MUI의 제어 컴포넌트 패턴 내장 지원
|
|
116
164
|
|
|
117
165
|
**⚡ Performance-Critical Forms**
|
|
118
166
|
**성능이 중요한 폼**
|
|
119
167
|
|
|
120
|
-
-
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
|
|
124
|
-
-
|
|
125
|
-
|
|
168
|
+
- Large forms with many fields (50+ inputs)
|
|
169
|
+
많은 필드가 있는 대규모 폼 (50개 이상 입력)
|
|
170
|
+
- Real-time data visualization forms
|
|
171
|
+
실시간 데이터 시각화 폼
|
|
172
|
+
- Forms that update frequently during user interaction
|
|
173
|
+
사용자 상호작용 중 자주 업데이트되는 폼
|
|
126
174
|
|
|
127
175
|
**🔄 Multi-Step & Global Forms**
|
|
128
176
|
**멀티 스텝 & 글로벌 폼**
|
|
129
177
|
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
-
|
|
133
|
-
|
|
134
|
-
-
|
|
135
|
-
|
|
178
|
+
- Wizard-style multi-step forms
|
|
179
|
+
마법사 스타일의 멀티 스텝 폼
|
|
180
|
+
- Forms shared across multiple components
|
|
181
|
+
여러 컴포넌트에서 공유되는 폼
|
|
182
|
+
- Registration flows with data persistence
|
|
183
|
+
데이터 지속성이 있는 등록 플로우
|
|
136
184
|
|
|
137
185
|
**🏗️ Complex Nested Data**
|
|
138
186
|
**복잡한 중첩 데이터**
|
|
139
187
|
|
|
140
|
-
-
|
|
141
|
-
|
|
142
|
-
-
|
|
143
|
-
|
|
144
|
-
-
|
|
145
|
-
|
|
188
|
+
- User profiles with nested address/contact info
|
|
189
|
+
중첩된 주소/연락처 정보가 있는 사용자 프로필
|
|
190
|
+
- Product configurations with multiple layers
|
|
191
|
+
다층 구조의 제품 구성
|
|
192
|
+
- Settings panels with grouped options
|
|
193
|
+
그룹화된 옵션이 있는 설정 패널
|
|
146
194
|
|
|
147
195
|
**📊 Dynamic Form Generation**
|
|
148
196
|
**동적 폼 생성**
|
|
149
197
|
|
|
150
|
-
-
|
|
151
|
-
|
|
152
|
-
-
|
|
153
|
-
|
|
154
|
-
-
|
|
155
|
-
|
|
198
|
+
- Forms generated from API schemas
|
|
199
|
+
API 스키마에서 생성되는 폼
|
|
200
|
+
- Conditional field rendering
|
|
201
|
+
조건부 필드 렌더링
|
|
202
|
+
- Dynamic validation rules
|
|
203
|
+
동적 검증 규칙
|
|
156
204
|
|
|
157
205
|
## 🎯 핵심 성능 원칙 | Core Performance Principles
|
|
158
206
|
|
|
@@ -168,15 +216,15 @@ const userEmail = form.useFormValue("user.email");
|
|
|
168
216
|
|
|
169
217
|
## 🌐 링크 | Links
|
|
170
218
|
|
|
171
|
-
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
219
|
+
- **📦 NPM**: [https://www.npmjs.com/package/@ehfuse/forma](https://www.npmjs.com/package/@ehfuse/forma)
|
|
220
|
+
- **🐙 GitHub**: [https://github.com/ehfuse/forma](https://github.com/ehfuse/forma)
|
|
221
|
+
- **📄 라이선스 | License**: [MIT](./LICENSE)
|
|
174
222
|
|
|
175
223
|
## 📞 연락처 | Contact
|
|
176
224
|
|
|
177
|
-
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
225
|
+
- **개발자 | Developer**: 김영진 (KIM YOUNG JIN)
|
|
226
|
+
- **이메일 | Email**: ehfuse@gmail.com
|
|
227
|
+
- **GitHub**: [@ehfuse](https://github.com/ehfuse)
|
|
180
228
|
|
|
181
229
|
---
|
|
182
230
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFieldState.ts
|
|
3
|
+
*
|
|
4
|
+
* Advanced state management hook with individual field subscriptions
|
|
5
|
+
* Optimized for arrays, objects, and complex nested data structures
|
|
6
|
+
*
|
|
7
|
+
* @author KIM YOUNG JIN (ehfuse@gmail.com)
|
|
8
|
+
* @license MIT License
|
|
9
|
+
*/
|
|
10
|
+
import { FieldStore } from "../core/FieldStore";
|
|
11
|
+
/**
|
|
12
|
+
* Options for configuring useFieldState hook
|
|
13
|
+
* useFieldState 훅 설정을 위한 옵션
|
|
14
|
+
*/
|
|
15
|
+
export interface UseFieldStateOptions<T extends Record<string, any>> {
|
|
16
|
+
/** Optional callback when state changes | 상태 변경 시 선택적 콜백 */
|
|
17
|
+
onChange?: (values: T) => void;
|
|
18
|
+
/** Enable deep equality checking for better performance | 성능 향상을 위한 깊은 동등성 검사 활성화 */
|
|
19
|
+
deepEquals?: boolean;
|
|
20
|
+
/** External FieldStore instance for shared state | 공유 상태를 위한 외부 FieldStore 인스턴스 */
|
|
21
|
+
_externalStore?: FieldStore<T>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Return type of useFieldState hook
|
|
25
|
+
* useFieldState 훅의 반환 타입
|
|
26
|
+
*/
|
|
27
|
+
export interface UseFieldStateReturn<T extends Record<string, any>> {
|
|
28
|
+
/** Subscribe to a specific field value with dot notation | dot notation으로 특정 필드 값 구독 */
|
|
29
|
+
useValue: <K extends string>(path: K) => any;
|
|
30
|
+
/** Set a specific field value with dot notation | dot notation으로 특정 필드 값 설정 */
|
|
31
|
+
setValue: <K extends string>(path: K, value: any) => void;
|
|
32
|
+
/** Get all current values (non-reactive) | 모든 현재 값 가져오기 (반응형 아님) */
|
|
33
|
+
getValues: () => T;
|
|
34
|
+
/** Set all values at once | 모든 값을 한 번에 설정 */
|
|
35
|
+
setValues: (values: Partial<T>) => void;
|
|
36
|
+
/** Reset to initial values | 초기값으로 재설정 */
|
|
37
|
+
reset: () => void;
|
|
38
|
+
/** Handle standard input change events | 표준 입력 변경 이벤트 처리 */
|
|
39
|
+
handleChange: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => void;
|
|
40
|
+
/** Direct access to the internal store for advanced usage | 고급 사용을 위한 내부 스토어 직접 접근 */
|
|
41
|
+
_store: FieldStore<T>;
|
|
42
|
+
/** Current values (reactive) | 현재 값들 (반응형) */
|
|
43
|
+
values: T;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Advanced state management hook with individual field subscriptions
|
|
47
|
+
* 개별 필드 구독을 통한 고급 상태 관리 훅
|
|
48
|
+
*
|
|
49
|
+
* Optimized for managing complex arrays, objects, and nested data structures
|
|
50
|
+
* where you want to avoid unnecessary re-renders when only specific fields change.
|
|
51
|
+
*
|
|
52
|
+
* 복잡한 배열, 객체, 중첩된 데이터 구조를 관리하는 데 최적화되어 있으며,
|
|
53
|
+
* 특정 필드만 변경될 때 불필요한 재렌더링을 방지하고자 할 때 사용합니다.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* // Managing an array of users
|
|
58
|
+
* const state = useFieldState({
|
|
59
|
+
* initialValues: {
|
|
60
|
+
* users: [
|
|
61
|
+
* { name: 'John', email: 'john@example.com', age: 30 },
|
|
62
|
+
* { name: 'Jane', email: 'jane@example.com', age: 25 }
|
|
63
|
+
* ],
|
|
64
|
+
* settings: { theme: 'dark', notifications: true }
|
|
65
|
+
* }
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* // Subscribe to individual fields - only these components re-render when changed
|
|
69
|
+
* const firstName = state.useValue('users.0.name'); // Only re-renders when John's name changes
|
|
70
|
+
* const userAge = state.useValue('users.1.age'); // Only re-renders when Jane's age changes
|
|
71
|
+
* const theme = state.useValue('settings.theme'); // Only re-renders when theme changes
|
|
72
|
+
*
|
|
73
|
+
* // Update specific fields
|
|
74
|
+
* state.setValue('users.0.name', 'Johnny');
|
|
75
|
+
* state.setValue('settings.theme', 'light');
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare function useFieldState<T extends Record<string, any>>(initialValues: T, options?: UseFieldStateOptions<T>): UseFieldStateReturn<T>;
|
|
79
|
+
//# sourceMappingURL=useFieldState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFieldState.d.ts","sourceRoot":"","sources":["../../../hooks/useFieldState.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD;;;GAGG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAC/D,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;IAE/B,qFAAqF;IACrF,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,mFAAmF;IACnF,cAAc,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAC9D,wFAAwF;IACxF,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,GAAG,CAAC;IAE7C,+EAA+E;IAC/E,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAE1D,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC,CAAC;IAEnB,6CAA6C;IAC7C,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAExC,0CAA0C;IAC1C,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,4DAA4D;IAC5D,YAAY,EAAE,CACV,KAAK,EAAE,KAAK,CAAC,WAAW,CACpB,gBAAgB,GAAG,mBAAmB,GAAG,iBAAiB,CAC7D,KACA,IAAI,CAAC;IAEV,sFAAsF;IACtF,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAEtB,8CAA8C;IAC9C,MAAM,EAAE,CAAC,CAAC;CACb;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACvD,aAAa,EAAE,CAAC,EAChB,OAAO,GAAE,oBAAoB,CAAC,CAAC,CAAM,GACtC,mBAAmB,CAAC,CAAC,CAAC,CA2HxB"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFieldState.ts
|
|
3
|
+
*
|
|
4
|
+
* Advanced state management hook with individual field subscriptions
|
|
5
|
+
* Optimized for arrays, objects, and complex nested data structures
|
|
6
|
+
*
|
|
7
|
+
* @author KIM YOUNG JIN (ehfuse@gmail.com)
|
|
8
|
+
* @license MIT License
|
|
9
|
+
*/
|
|
10
|
+
import { useRef, useCallback, useMemo, useState, useEffect } from "react";
|
|
11
|
+
import { FieldStore } from "../core/FieldStore";
|
|
12
|
+
import { setNestedValue } from "../utils/dotNotation";
|
|
13
|
+
/**
|
|
14
|
+
* Individual field subscription for useFieldState
|
|
15
|
+
* 개별 필드 구독을 위한 내부 함수
|
|
16
|
+
*/
|
|
17
|
+
function useFieldValue(store, fieldName) {
|
|
18
|
+
const [value, setValue] = useState(() => store.getValue(fieldName));
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
// 구독 설정 / Setup subscription
|
|
21
|
+
const unsubscribe = store.subscribe(fieldName, () => {
|
|
22
|
+
const newValue = store.getValue(fieldName);
|
|
23
|
+
setValue(newValue);
|
|
24
|
+
});
|
|
25
|
+
return unsubscribe;
|
|
26
|
+
}, [fieldName]); // store 의존성 제거로 불필요한 재구독 방지 / Remove store dependency to prevent unnecessary re-subscriptions
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Advanced state management hook with individual field subscriptions
|
|
31
|
+
* 개별 필드 구독을 통한 고급 상태 관리 훅
|
|
32
|
+
*
|
|
33
|
+
* Optimized for managing complex arrays, objects, and nested data structures
|
|
34
|
+
* where you want to avoid unnecessary re-renders when only specific fields change.
|
|
35
|
+
*
|
|
36
|
+
* 복잡한 배열, 객체, 중첩된 데이터 구조를 관리하는 데 최적화되어 있으며,
|
|
37
|
+
* 특정 필드만 변경될 때 불필요한 재렌더링을 방지하고자 할 때 사용합니다.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // Managing an array of users
|
|
42
|
+
* const state = useFieldState({
|
|
43
|
+
* initialValues: {
|
|
44
|
+
* users: [
|
|
45
|
+
* { name: 'John', email: 'john@example.com', age: 30 },
|
|
46
|
+
* { name: 'Jane', email: 'jane@example.com', age: 25 }
|
|
47
|
+
* ],
|
|
48
|
+
* settings: { theme: 'dark', notifications: true }
|
|
49
|
+
* }
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* // Subscribe to individual fields - only these components re-render when changed
|
|
53
|
+
* const firstName = state.useValue('users.0.name'); // Only re-renders when John's name changes
|
|
54
|
+
* const userAge = state.useValue('users.1.age'); // Only re-renders when Jane's age changes
|
|
55
|
+
* const theme = state.useValue('settings.theme'); // Only re-renders when theme changes
|
|
56
|
+
*
|
|
57
|
+
* // Update specific fields
|
|
58
|
+
* state.setValue('users.0.name', 'Johnny');
|
|
59
|
+
* state.setValue('settings.theme', 'light');
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function useFieldState(initialValues, options = {}) {
|
|
63
|
+
const { onChange, deepEquals = false, _externalStore } = options;
|
|
64
|
+
// Create or use external field store instance (persists across renders)
|
|
65
|
+
// 필드 스토어 인스턴스 생성 또는 외부 스토어 사용 (렌더링 간 유지)
|
|
66
|
+
const storeRef = useRef(null);
|
|
67
|
+
if (_externalStore) {
|
|
68
|
+
storeRef.current = _externalStore;
|
|
69
|
+
}
|
|
70
|
+
else if (!storeRef.current) {
|
|
71
|
+
storeRef.current = new FieldStore(initialValues);
|
|
72
|
+
// Set up global change listener if provided
|
|
73
|
+
// 글로벌 변경 리스너 설정 (제공된 경우)
|
|
74
|
+
if (onChange) {
|
|
75
|
+
storeRef.current.subscribeGlobal(() => {
|
|
76
|
+
onChange(storeRef.current.getValues());
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const store = storeRef.current;
|
|
81
|
+
// Update initial values when they change (only for non-external stores)
|
|
82
|
+
// 초기값이 변경되면 업데이트 (외부 스토어가 아닌 경우에만)
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (!_externalStore && store) {
|
|
85
|
+
store.setInitialValues(initialValues);
|
|
86
|
+
}
|
|
87
|
+
}, [initialValues, store, _externalStore]);
|
|
88
|
+
// Subscribe to a specific field value with dot notation
|
|
89
|
+
// dot notation으로 특정 필드 값 구독
|
|
90
|
+
const useValue = useCallback((path) => {
|
|
91
|
+
return useFieldValue(store, path);
|
|
92
|
+
}, [store]);
|
|
93
|
+
// Set a specific field value with dot notation
|
|
94
|
+
// dot notation으로 특정 필드 값 설정
|
|
95
|
+
const setValue = useCallback((path, value) => {
|
|
96
|
+
const currentValues = store.getValues();
|
|
97
|
+
const newValues = setNestedValue(currentValues, path, value);
|
|
98
|
+
store.setValues(newValues);
|
|
99
|
+
}, [store]);
|
|
100
|
+
// Get all current values (non-reactive)
|
|
101
|
+
// 모든 현재 값 가져오기 (반응형 아님)
|
|
102
|
+
const getValues = useCallback(() => {
|
|
103
|
+
return store.getValues();
|
|
104
|
+
}, [store]);
|
|
105
|
+
// Set all values at once
|
|
106
|
+
// 모든 값을 한 번에 설정
|
|
107
|
+
const setValues = useCallback((values) => {
|
|
108
|
+
const currentValues = store.getValues();
|
|
109
|
+
const newValues = { ...currentValues, ...values };
|
|
110
|
+
store.setValues(newValues);
|
|
111
|
+
}, [store]);
|
|
112
|
+
// Reset to initial values
|
|
113
|
+
// 초기값으로 재설정
|
|
114
|
+
const reset = useCallback(() => {
|
|
115
|
+
store.setValues(initialValues);
|
|
116
|
+
}, [store, initialValues]);
|
|
117
|
+
// Handle standard input change events
|
|
118
|
+
// 표준 입력 변경 이벤트 처리
|
|
119
|
+
const handleChange = useCallback((event) => {
|
|
120
|
+
const { name, value, type } = event.target;
|
|
121
|
+
if (!name) {
|
|
122
|
+
console.warn('useFieldState.handleChange: input element must have a "name" attribute');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
let processedValue = value;
|
|
126
|
+
// Handle different input types
|
|
127
|
+
// 다양한 입력 타입 처리
|
|
128
|
+
if (type === "checkbox") {
|
|
129
|
+
processedValue = event.target.checked;
|
|
130
|
+
}
|
|
131
|
+
else if (type === "number") {
|
|
132
|
+
processedValue = value === "" ? "" : Number(value);
|
|
133
|
+
}
|
|
134
|
+
setValue(name, processedValue);
|
|
135
|
+
}, [setValue]);
|
|
136
|
+
// Get reactive values using the store's reactive mechanism
|
|
137
|
+
// 스토어의 반응형 메커니즘을 사용하여 반응형 값 가져오기
|
|
138
|
+
const values = useMemo(() => {
|
|
139
|
+
// This creates a reactive subscription to all values
|
|
140
|
+
// 모든 값에 대한 반응형 구독 생성
|
|
141
|
+
return store.getValues();
|
|
142
|
+
}, [store]);
|
|
143
|
+
return {
|
|
144
|
+
useValue,
|
|
145
|
+
setValue,
|
|
146
|
+
getValues,
|
|
147
|
+
setValues,
|
|
148
|
+
reset,
|
|
149
|
+
handleChange,
|
|
150
|
+
_store: store,
|
|
151
|
+
values,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=useFieldState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFieldState.js","sourceRoot":"","sources":["../../../hooks/useFieldState.ts"],"names":[],"mappings":"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"}
|
|
@@ -50,7 +50,7 @@ export declare function useForm<T extends Record<string, any>>({ initialValues,
|
|
|
50
50
|
isSubmitting: boolean;
|
|
51
51
|
isValidating: boolean;
|
|
52
52
|
isModified: boolean;
|
|
53
|
-
useFormValue: (fieldName: string) =>
|
|
53
|
+
useFormValue: (fieldName: string) => any;
|
|
54
54
|
getFormValue: (fieldName: string) => any;
|
|
55
55
|
getFormValues: () => T;
|
|
56
56
|
setFormValue: (name: string, value: any) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useForm.d.ts","sourceRoot":"","sources":["../../../hooks/useForm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACH,eAAe,EACf,uBAAuB,EACvB,YAAY,EACf,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"useForm.d.ts","sourceRoot":"","sources":["../../../hooks/useForm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACH,eAAe,EACf,uBAAuB,EACvB,YAAY,EACf,MAAM,eAAe,CAAC;AAGvB,OAAO,KAMN,MAAM,OAAO,CAAC;AAKf;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACnD,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,cAAc,GACjB,EAAE,YAAY,CAAC,CAAC,CAAC;;;;8BAuIE,MAAM;8BAnBN,MAAM,KAAG,GAAG;yBASU,CAAC;yBAhD5B,MAAM,SAAS,GAAG;+BAmBb,OAAO,CAAC,CAAC,CAAC;6CAUH,CAAC;0BApFhB,eAAe;;iBAyJR,KAAK,CAAC,SAAS,KAAG,OAAO,CAAC,OAAO,CAAC;;sCA9BnB,CAAC;;;EA8GlC"}
|