react-form-manage 1.0.8-beta.2 → 1.0.8-beta.21

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +85 -5
  2. package/README.md +8 -4
  3. package/dist/components/Form/FormCleanUp.js +3 -3
  4. package/dist/components/Form/FormItem.d.ts +4 -2
  5. package/dist/components/Form/FormItem.js +7 -5
  6. package/dist/components/Form/FormList.d.ts +2 -2
  7. package/dist/components/Form/FormList.js +2 -2
  8. package/dist/hooks/useFormItemControl.d.ts +1 -0
  9. package/dist/hooks/useFormItemControl.js +60 -23
  10. package/dist/hooks/useFormListControl.d.ts +2 -1
  11. package/dist/hooks/useFormListControl.js +60 -10
  12. package/dist/index.cjs.d.ts +1 -0
  13. package/dist/index.d.ts +4 -3
  14. package/dist/index.esm.d.ts +1 -0
  15. package/dist/index.js +4 -2
  16. package/dist/providers/Form.d.ts +4 -2
  17. package/dist/providers/Form.js +98 -28
  18. package/dist/stores/formStore.d.ts +27 -2
  19. package/dist/stores/formStore.js +25 -9
  20. package/dist/test/TestDialog.d.ts +3 -0
  21. package/dist/test/TestDialog.js +21 -0
  22. package/dist/test/TestListener.d.ts +3 -0
  23. package/dist/test/TestListener.js +17 -0
  24. package/dist/test/TestSelect.d.ts +6 -0
  25. package/dist/test/TestSelect.js +24 -0
  26. package/dist/types/index.d.ts +1 -1
  27. package/dist/types/public.d.ts +6 -1
  28. package/dist/utils/obj.util.d.ts +1 -1
  29. package/dist/utils/obj.util.js +16 -5
  30. package/package.json +3 -1
  31. package/src/App.tsx +98 -4
  32. package/src/components/Form/FormCleanUp.tsx +3 -7
  33. package/src/components/Form/FormItem.tsx +56 -31
  34. package/src/components/Form/FormList.tsx +17 -4
  35. package/src/components/Form/InputWrapper.tsx +5 -0
  36. package/src/hooks/useFormItemControl.ts +70 -29
  37. package/src/hooks/useFormListControl.ts +104 -26
  38. package/src/index.ts +27 -13
  39. package/src/providers/Form.tsx +126 -17
  40. package/src/stores/formStore.ts +319 -288
  41. package/src/test/TestDialog.tsx +52 -0
  42. package/src/test/TestListener.tsx +21 -0
  43. package/src/test/TestSelect.tsx +38 -0
  44. package/src/types/index.ts +1 -1
  45. package/src/types/public.ts +7 -1
  46. package/src/utils/obj.util.ts +44 -13
@@ -1,20 +1,16 @@
1
1
  import { useEffect } from "react";
2
2
  import { useShallow } from "zustand/react/shallow";
3
- import {
4
- useFormCleanUp,
5
- useFormListeners,
6
- useFormStore,
7
- } from "../../stores/formStore";
3
+ import { useFormStore } from "../../stores/formStore";
8
4
 
9
5
  const FormCleanUp = () => {
10
- const { cleanUpStack, clearCleanUpStack } = useFormCleanUp(
6
+ const { cleanUpStack, clearCleanUpStack } = useFormStore(
11
7
  useShallow((state) => ({
12
8
  cleanUpStack: state.cleanUpStack,
13
9
  clearCleanUpStack: state.clearCleanUpStack,
14
10
  })),
15
11
  );
16
12
 
17
- const { revokeListener } = useFormListeners(
13
+ const { revokeListener } = useFormStore(
18
14
  useShallow((state) => ({
19
15
  revokeListener: state.revokeListener,
20
16
  })),
@@ -1,11 +1,11 @@
1
1
  import type { ReactElement } from "react";
2
- import { cloneElement, useRef, useState } from "react";
2
+ import { cloneElement, Fragment, useRef, useState } from "react";
3
3
  import { v4 } from "uuid";
4
4
  import useFormItemControl from "../../hooks/useFormItemControl";
5
5
  import type { ValidationRule } from "../../types/public";
6
6
 
7
7
  export interface FormItemProps {
8
- children: ReactElement;
8
+ children: ReactElement<any>;
9
9
  name: string;
10
10
  formName?: string;
11
11
  initialValue?: any;
@@ -13,6 +13,8 @@ export interface FormItemProps {
13
13
  rules?: ValidationRule[];
14
14
  valuePropName?: string;
15
15
  getValueFromEvent?: (...args: any[]) => any;
16
+ controlAfterInit?: boolean;
17
+ hidden?: boolean;
16
18
  }
17
19
 
18
20
  export default function FormItem({
@@ -24,6 +26,8 @@ export default function FormItem({
24
26
  rules,
25
27
  valuePropName = "value",
26
28
  getValueFromEvent,
29
+ controlAfterInit = false,
30
+ hidden,
27
31
  }: FormItemProps) {
28
32
  const elRef = useRef<any>(null);
29
33
 
@@ -37,6 +41,7 @@ export default function FormItem({
37
41
  isDirty,
38
42
  submitState,
39
43
  isTouched,
44
+ isInitied,
40
45
  } = useFormItemControl({
41
46
  formName,
42
47
  name,
@@ -51,34 +56,54 @@ export default function FormItem({
51
56
  // console.log({ value });
52
57
  // }, [value]);
53
58
 
54
- return cloneElement(children, {
55
- // ref: inputRef,
56
- [valuePropName]: value,
57
- onChange: (...args: any[]) => {
58
- let val = args[0];
59
- if (getValueFromEvent && typeof getValueFromEvent === "function") {
60
- val = getValueFromEvent(...args);
61
- } else {
62
- const e = args[0];
63
- if (e && e.target) {
64
- val = e.target.value;
65
- }
66
- }
67
- onChange(val);
68
- },
69
- // onFocus: () => {
70
- // setIsTouched(true);
71
- // },
72
- // isTouched: isTouched,
73
- isDirty: isDirty,
74
- // errors: errors,
75
- // formState,
59
+ // useEffect(() => {
60
+ // console.log("isInitied changed: ", {
61
+ // isInitied,
62
+ // name,
63
+ // value,
64
+ // controlAfterInit,
65
+ // });
66
+ // }, [isInitied]);
67
+ return (
68
+ <Fragment
69
+ key={`control-after-init-${Boolean(controlAfterInit && isInitied) ? "1" : "0"}-${formItemId}`}
70
+ >
71
+ {!hidden && children
72
+ ? cloneElement(children, {
73
+ name,
74
+ // ref: inputRef,
75
+ [valuePropName]: value,
76
+ onChange: (...args: any[]) => {
77
+ let val = args[0];
78
+ if (
79
+ getValueFromEvent &&
80
+ typeof getValueFromEvent === "function"
81
+ ) {
82
+ val = getValueFromEvent(...args);
83
+ } else {
84
+ const e = args[0];
85
+ if (e && e.target) {
86
+ val = e.target.value;
87
+ }
88
+ }
89
+ onChange(val);
90
+ },
91
+ // onFocus: () => {
92
+ // setIsTouched(true);
93
+ // },
94
+ // isTouched: isTouched,
95
+ isDirty: isDirty,
96
+ // errors: errors,
97
+ // formState,
76
98
 
77
- errors,
78
- onFocus,
79
- validateState: state,
80
- ref: elRef,
81
- submitState,
82
- isTouched: isTouched,
83
- } as any);
99
+ errors,
100
+ onFocus,
101
+ validateState: state,
102
+ ref: elRef,
103
+ submitState,
104
+ isTouched: isTouched,
105
+ } as any)
106
+ : null}
107
+ </Fragment>
108
+ );
84
109
  }
@@ -1,4 +1,4 @@
1
- import useTestRrenderListControl from "../../hooks/useFormListControl";
1
+ import useFormListControl from "../../hooks/useFormListControl";
2
2
  import type { FormInstance } from "../../stores/formStore";
3
3
 
4
4
  export interface FormListProps<T = any> {
@@ -6,11 +6,24 @@ export interface FormListProps<T = any> {
6
6
  initialValues?: T[];
7
7
  form?: FormInstance;
8
8
  formName?: string;
9
- children: (fields: Array<{ name: string; key: string }>, operations: { add: (index: number) => void; remove: (opts: { index?: number; key?: string }) => void; move: (opts: { from?: number; fromKey?: string; to: number }) => void }) => React.ReactNode;
9
+ children: (
10
+ fields: Array<{ name: string; key: string }>,
11
+ operations: {
12
+ add: (index?: number) => void;
13
+ remove: (opts: { index?: number; key?: string }) => void;
14
+ move: (opts: { from?: number; fromKey?: string; to: number }) => void;
15
+ },
16
+ ) => React.ReactNode;
10
17
  }
11
18
 
12
- const FormList = <T = any>({ name, initialValues, form, formName, children }: FormListProps<T>) => {
13
- const { listFields, ...actions } = useTestRrenderListControl({
19
+ const FormList = <T = any,>({
20
+ name,
21
+ initialValues,
22
+ form,
23
+ formName,
24
+ children,
25
+ }: FormListProps<T>) => {
26
+ const { listFields, ...actions } = useFormListControl({
14
27
  name,
15
28
  initialValues,
16
29
  form,
@@ -13,6 +13,11 @@ const InputWrapper = ({
13
13
  errors = [],
14
14
  ...props
15
15
  }: InputWrapperProps) => {
16
+ // useEffect(() => {
17
+ // console.log("InputWrapper submitState changed: ", {
18
+ // submitState: props.submitState,
19
+ // });
20
+ // }, [props.submitState]);
16
21
  return (
17
22
  <div>
18
23
  {cloneElement(children, props)}
@@ -1,4 +1,4 @@
1
- import { get, isNil } from "lodash";
1
+ import { get, has, isNil } from "lodash";
2
2
  import { useTaskEffect } from "minh-custom-hooks-release";
3
3
  import { useEffect, useMemo, type RefObject } from "react";
4
4
  import { useShallow } from "zustand/react/shallow"; // Import useShallow
@@ -18,11 +18,7 @@ import {
18
18
  IS_VIETNAMESE_PHONE_NUMBER_REGEX,
19
19
  } from "../constants/validation";
20
20
  import { useFormContext } from "../providers/Form";
21
- import {
22
- useFormCleanUp,
23
- useFormListeners,
24
- useFormStore,
25
- } from "../stores/formStore";
21
+ import { useFormStore } from "../stores/formStore";
26
22
 
27
23
  import type { FormInstance } from "../stores/formStore";
28
24
  import type {
@@ -52,6 +48,7 @@ export interface UseFormItemControlReturn {
52
48
  isDirty?: boolean;
53
49
  isTouched?: boolean;
54
50
  submitState?: SubmitState;
51
+ isInitied?: boolean;
55
52
  }
56
53
 
57
54
  const VALID_PREMITIVE_TYPE = ["string", "number", "undefined"];
@@ -71,7 +68,7 @@ export default function useFormItemControl<T = any>({
71
68
  setData,
72
69
  getCacheData,
73
70
  getFormValues,
74
- getFormState,
71
+ // getFormState,
75
72
  isStateInitied,
76
73
  submitState,
77
74
  } = useFormStore(
@@ -84,7 +81,7 @@ export default function useFormItemControl<T = any>({
84
81
  setData: state.setData,
85
82
  getCacheData: state.getCacheData,
86
83
  getFormValues: state.getFormValues,
87
- getFormState: state.getFormState,
84
+ // getFormState: state.getFormState,
88
85
  isStateInitied:
89
86
  state.formStates?.[
90
87
  formName || form?.formName || contextForm?.formName
@@ -97,7 +94,7 @@ export default function useFormItemControl<T = any>({
97
94
  }),
98
95
  );
99
96
 
100
- const { setCleanUpStack } = useFormCleanUp(
97
+ const { setCleanUpStack } = useFormStore(
101
98
  useShallow((state) => ({
102
99
  setCleanUpStack: state.setCleanUpStack,
103
100
  })),
@@ -129,7 +126,7 @@ export default function useFormItemControl<T = any>({
129
126
  }),
130
127
  );
131
128
 
132
- const { listener, setListener } = useFormListeners(
129
+ const { listener, setListener } = useFormStore(
133
130
  useShallow((state) => {
134
131
  // console.log(
135
132
  // "Get listener from store: ",
@@ -154,7 +151,7 @@ export default function useFormItemControl<T = any>({
154
151
  name,
155
152
  value,
156
153
  );
157
- onChange(value, { notTriggerDirty: true });
154
+ onChange(value, { notTriggerDirty: true, initiedData: true });
158
155
  };
159
156
 
160
157
  const onFocus = () => {
@@ -171,7 +168,13 @@ export default function useFormItemControl<T = any>({
171
168
  }
172
169
  };
173
170
 
174
- const onChange = (value: T, options?: any) => {
171
+ const onChange = (
172
+ value: T,
173
+ options?: {
174
+ notTriggerDirty?: boolean;
175
+ initiedData?: boolean;
176
+ },
177
+ ) => {
175
178
  if (options?.notTriggerDirty !== true) {
176
179
  setListener({
177
180
  formItemId,
@@ -179,6 +182,12 @@ export default function useFormItemControl<T = any>({
179
182
  isTouched: listener?.isTouched,
180
183
  });
181
184
  }
185
+ if (options?.initiedData === true) {
186
+ setListener({
187
+ formItemId,
188
+ isInitied: true,
189
+ });
190
+ }
182
191
  setData(formName || form?.formName || contextForm?.formName, name, value);
183
192
  };
184
193
 
@@ -188,10 +197,16 @@ export default function useFormItemControl<T = any>({
188
197
  value,
189
198
  getInitData(formName || form?.formName || contextForm?.formName, name),
190
199
  );
200
+ setListener({
201
+ formItemId,
202
+ isDirty: false,
203
+ isTouched: false,
204
+ });
191
205
  onChange(
192
206
  isNil(value)
193
207
  ? getInitData(formName || form?.formName || contextForm?.formName, name)
194
208
  : value,
209
+ { notTriggerDirty: true, initiedData: true },
195
210
  );
196
211
  };
197
212
 
@@ -200,8 +215,17 @@ export default function useFormItemControl<T = any>({
200
215
  return rules || [];
201
216
  }, [rules]);
202
217
 
218
+ useEffect(() => {
219
+ console.log("Rules changed: ", { name, listener });
220
+ }, [value, listener]);
221
+
203
222
  const { data: errors, state } = useTaskEffect({
204
223
  async task() {
224
+ console.log("Trigger validate for form item: ", {
225
+ formName: formName || form?.formName || contextForm?.formName,
226
+ name,
227
+ value,
228
+ });
205
229
  const listErrors = [];
206
230
 
207
231
  const formValues = getFormValues(
@@ -494,7 +518,9 @@ export default function useFormItemControl<T = any>({
494
518
 
495
519
  return listErrors;
496
520
  },
497
- deps: [internalRules, value],
521
+ deps: [internalRules, value, listener?.isInitied],
522
+ enabled: Boolean(listener?.isInitied),
523
+
498
524
  onError(err) {
499
525
  console.log(err);
500
526
  },
@@ -522,8 +548,16 @@ export default function useFormItemControl<T = any>({
522
548
  onInitData(initialValue);
523
549
  }
524
550
  } else {
525
- onChange(internalInitValue, { notTriggerDirty: true });
551
+ onChange(internalInitValue, {
552
+ notTriggerDirty: true,
553
+ initiedData: true,
554
+ });
526
555
  }
556
+ } else {
557
+ onChange(value, {
558
+ notTriggerDirty: true,
559
+ initiedData: true,
560
+ });
527
561
  }
528
562
  return;
529
563
  }
@@ -543,6 +577,15 @@ export default function useFormItemControl<T = any>({
543
577
  });
544
578
  }
545
579
 
580
+ return () => {
581
+ if (listener) {
582
+ setCleanUpStack({
583
+ name: listener.name,
584
+ itemKey: listener.formItemId,
585
+ });
586
+ }
587
+ };
588
+
546
589
  // return () => {
547
590
  // console.log("Revoke listener", listener);
548
591
 
@@ -568,28 +611,25 @@ export default function useFormItemControl<T = any>({
568
611
  // console.log("Get cache Data after list change: ", cacheData);
569
612
 
570
613
  if (cacheData) {
614
+ console.log("Cache data found when form item change: ", name, cacheData);
571
615
  const getNewDataFromCache = get(cacheData, name);
616
+ const isIncludeDirectoryInCache = has(cacheData, name);
572
617
 
573
618
  // console.log("Init data when change form ite: ", name, cacheData);
574
619
 
575
- if (!getNewDataFromCache) {
576
- onChange(initialValue);
577
- } else onChange(getNewDataFromCache);
620
+ if (!isIncludeDirectoryInCache && isNil(getNewDataFromCache)) {
621
+ onChange(initialValue, {
622
+ notTriggerDirty: true,
623
+ initiedData: true,
624
+ });
625
+ } else
626
+ onChange(getNewDataFromCache, {
627
+ notTriggerDirty: true,
628
+ initiedData: true,
629
+ });
578
630
  }
579
631
  }, [name, formName || form?.formName || contextForm?.formName]);
580
632
 
581
- useEffect(() => {
582
- return () => {
583
- setCleanUpStack({
584
- itemKey: formItemId,
585
- });
586
- };
587
- }, []);
588
-
589
- useEffect(() => {
590
- console.log("Submit state changed in useFormItemControl: ", submitState);
591
- }, [submitState]);
592
-
593
633
  return {
594
634
  value: value as T,
595
635
  onChange,
@@ -599,5 +639,6 @@ export default function useFormItemControl<T = any>({
599
639
  isDirty: listener?.isDirty,
600
640
  isTouched: listener?.isTouched,
601
641
  submitState,
642
+ isInitied: listener?.isInitied,
602
643
  };
603
644
  }
@@ -3,11 +3,11 @@ import { useEffect, useState } from "react";
3
3
  import { v4 } from "uuid";
4
4
  import { useShallow } from "zustand/react/shallow";
5
5
  import { useFormContext } from "../providers/Form";
6
- import { useFormCleanUp, useFormStore } from "../stores/formStore";
6
+ import { useFormStore } from "../stores/formStore";
7
7
 
8
8
  import type { FormInstance } from "../stores/formStore";
9
9
 
10
- type ListField = { name: string; key: string };
10
+ type ListField = { name: string; key: string; value?: any };
11
11
 
12
12
  interface UseFormListControlProps {
13
13
  name?: string;
@@ -19,7 +19,7 @@ interface UseFormListControlProps {
19
19
  interface UseFormListControlReturn {
20
20
  listFields: ListField[];
21
21
  move: (opts: { from?: number; fromKey?: string; to: number }) => void;
22
- add: (index: number) => void;
22
+ add: (index?: number) => void;
23
23
  remove: (opts: { index?: number; key?: string }) => void;
24
24
  }
25
25
 
@@ -29,17 +29,26 @@ export default function useFormListControl<T = any>({
29
29
  initialValues,
30
30
  formName,
31
31
  }: UseFormListControlProps): UseFormListControlReturn {
32
+ const [formItemId] = useState<string>(v4());
32
33
  const contextForm = useFormContext();
33
34
  const getFormValues = useFormStore((state) => state.getFormValues);
34
- const [listFormInitValues, setListFormInitValues] = useState<any[] | undefined>(undefined);
35
- const { clearCacheData, setCacheData } = useFormStore(
36
- useShallow((state) => ({
37
- cacheData: state.cacheData,
38
- clearCacheData: state.clearCacheData,
39
- setCacheData: state.setCacheData,
40
- })),
41
- );
42
- const { setCleanUpStack } = useFormCleanUp(
35
+ const [listFormInitValues, setListFormInitValues] = useState<
36
+ any[] | undefined
37
+ >(undefined);
38
+ const { clearCacheData, setCacheData, setListener, getListener } =
39
+ useFormStore(
40
+ useShallow((state) => ({
41
+ // Cache
42
+ cacheData: state.cacheData,
43
+ clearCacheData: state.clearCacheData,
44
+ setCacheData: state.setCacheData,
45
+
46
+ // Listener
47
+ setListener: state.setListener,
48
+ getListener: state.getListener,
49
+ })),
50
+ );
51
+ const { setCleanUpStack } = useFormStore(
43
52
  useShallow((state) => ({
44
53
  setCleanUpStack: state.setCleanUpStack,
45
54
  })),
@@ -92,9 +101,20 @@ export default function useFormListControl<T = any>({
92
101
  })
93
102
  .filter(Boolean);
94
103
 
95
- const mapCurWithKey = cur.map(
96
- (c) => mapPrevWithKey.find((m) => m.key === c.key) || c,
97
- );
104
+ const mapCurWithKey = cur.map((c) => {
105
+ const find = mapPrevWithKey.find((m) => m.key === c.key);
106
+
107
+ if (find) {
108
+ return {
109
+ key: find.key,
110
+ value: isNil(c.value) ? find.value : c.value,
111
+ };
112
+ }
113
+
114
+ return c;
115
+ });
116
+
117
+ console.log("compare prev cur", { prev, cur });
98
118
 
99
119
  const getNewValueCache = mapCurWithKey.filter(Boolean).map((c) => c.value);
100
120
 
@@ -107,7 +127,7 @@ export default function useFormListControl<T = any>({
107
127
  // console.log("Mapping Cur value with prev fields: ", mapCurWithKey);
108
128
  // console.log("After change arr value: ", getNewValueCache);
109
129
 
110
- // Nếu số phần tử trước khi thay đổi mảng lớn hơn thì đẩy 2 phần tử còn lại vào clean up stack để clear
130
+ // Nếu số phần tử trước khi thay đổi mảng lớn hơn thì đẩy các phần tử còn lại vào clean up stack để clear
111
131
  if (startRemoveIndex > 0) {
112
132
  Array.from(Array(startRemoveIndex))
113
133
  .map((_, index) => {
@@ -127,6 +147,12 @@ export default function useFormListControl<T = any>({
127
147
  });
128
148
  }
129
149
 
150
+ console.log("Set cache data for form list: ", {
151
+ formName: formName || form?.formName || contextForm?.formName,
152
+ name,
153
+ getNewValueCache,
154
+ });
155
+
130
156
  // console.log({ getNewValueCache });
131
157
  setCacheData(
132
158
  formName || form?.formName || contextForm?.formName,
@@ -135,13 +161,9 @@ export default function useFormListControl<T = any>({
135
161
  );
136
162
  };
137
163
 
138
- const add = (index: number) => {
164
+ const add = (index?: number) => {
139
165
  setListFields((prev) => {
140
- if (index > prev.length) return prev;
141
-
142
- if (index < 0) return prev;
143
-
144
- if (index === prev.length) {
166
+ if (isNil(index) || index === prev.length) {
145
167
  const newName = `${name}.${prev.length}`;
146
168
  const newKey = v4();
147
169
  const result = [
@@ -154,6 +176,9 @@ export default function useFormListControl<T = any>({
154
176
 
155
177
  return result;
156
178
  }
179
+ if (index > prev.length) return prev;
180
+
181
+ if (index < 0) return prev;
157
182
 
158
183
  const clonePrev = [...prev];
159
184
 
@@ -217,9 +242,17 @@ export default function useFormListControl<T = any>({
217
242
  });
218
243
  };
219
244
 
220
- const move = ({ from, fromKey, to }: { from?: number; fromKey?: string; to: number }) => {
245
+ const move = ({
246
+ from,
247
+ fromKey,
248
+ to,
249
+ }: {
250
+ from?: number;
251
+ fromKey?: string;
252
+ to: number;
253
+ }) => {
221
254
  setListFields((prev) => {
222
- console.log("move list item: ", { from, to });
255
+ // console.log("move list item: ", { from, to });
223
256
  if (
224
257
  from >= listFields.length ||
225
258
  from < 0 ||
@@ -228,7 +261,7 @@ export default function useFormListControl<T = any>({
228
261
  from === to
229
262
  )
230
263
  return prev;
231
- console.log("Trigger move item: ");
264
+ // console.log("Trigger move item: ");
232
265
 
233
266
  if (!isNil(fromKey)) {
234
267
  const findItemIndex = prev.findIndex((p) => p.key === fromKey);
@@ -286,7 +319,7 @@ export default function useFormListControl<T = any>({
286
319
  useEffect(() => {
287
320
  console.log("Trigger init", formState?.isInitied, internalInitValue);
288
321
  if (formState?.isInitied) {
289
- if (Array.isArray(listFormInitValues)) {
322
+ if (Array.isArray(listFormInitValues)) {
290
323
  const result = listFormInitValues?.map((_, i) => {
291
324
  const itemName = `${name}.${i}`;
292
325
  const key = v4();
@@ -374,5 +407,50 @@ export default function useFormListControl<T = any>({
374
407
  };
375
408
  }, [listFields]);
376
409
 
410
+ useEffect(() => {
411
+ if (!getListener(formItemId)) {
412
+ setListener({
413
+ formName: formName || form?.formName || contextForm?.formName,
414
+ name: name || "",
415
+ formItemId: formItemId,
416
+ type: "array",
417
+ onArrayChange: (newArr) => {
418
+ setListFields((prev) => {
419
+ const result = newArr.map((_, i) => {
420
+ const itemName = `${name}.${i}`;
421
+ const existingItem = prev[i];
422
+ return {
423
+ key: existingItem ? existingItem.key : v4(),
424
+ name: itemName,
425
+ };
426
+ });
427
+
428
+ handleCacheListField(
429
+ prev,
430
+ result.map((r, i) => {
431
+ return { ...r, value: newArr[i] };
432
+ }),
433
+ );
434
+ return result;
435
+ });
436
+ },
437
+ });
438
+ }
439
+ return () => {
440
+ // Remove listener on unmount
441
+ if (getListener(formItemId)) {
442
+ console.log("Remove listener for form list: ", {
443
+ formItemId,
444
+ });
445
+ setListener({
446
+ formName: formName || form?.formName || contextForm?.formName,
447
+ name: name || "",
448
+ formItemId: formItemId,
449
+ onArrayChange: undefined,
450
+ });
451
+ }
452
+ };
453
+ }, []);
454
+
377
455
  return { listFields, move, add, remove };
378
456
  }