react-form-manage 1.0.8-beta.18 → 1.0.8-beta.20

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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.0.8-beta.19] - 2026-02-02
6
+
7
+ ### Features
8
+ - Trigger `initied` flag for onChange in `useFormItemControl` to properly mark form items as initialized
9
+
10
+ ## [1.0.8-beta.18] - 2026-02-01
11
+
12
+ ### Features
13
+ - Avoid deep traversal into class instances/functions in path collection
14
+ - `getAllNoneObjStringPath` now skips non-plain objects and functions, only traverses arrays and plain objects
15
+ - Improves performance and prevents accidental property access on class instances
16
+
5
17
  ## [1.0.8-beta.17] - 2026-02-01
6
18
 
7
19
  ### Features
@@ -8,14 +8,22 @@ import { useFormStore } from "../stores/formStore";
8
8
  const VALID_PREMITIVE_TYPE = ["string", "number", "undefined"];
9
9
  function useFormItemControl({ formName, form, name, initialValue, formItemId, rules, elementRef }) {
10
10
  const contextForm = useFormContext();
11
- const { value, setData, getCacheData, getFormValues, getFormState, isStateInitied, submitState } = useFormStore(useShallow((state2) => {
11
+ const {
12
+ value,
13
+ setData,
14
+ getCacheData,
15
+ getFormValues,
16
+ // getFormState,
17
+ isStateInitied,
18
+ submitState
19
+ } = useFormStore(useShallow((state2) => {
12
20
  var _a, _b, _c, _d;
13
21
  return {
14
22
  value: get(state2.forms, `${formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName)}.${name}`),
15
23
  setData: state2.setData,
16
24
  getCacheData: state2.getCacheData,
17
25
  getFormValues: state2.getFormValues,
18
- getFormState: state2.getFormState,
26
+ // getFormState: state.getFormState,
19
27
  isStateInitied: (_b = (_a = state2.formStates) == null ? void 0 : _a[formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName)]) == null ? void 0 : _b.isInitied,
20
28
  submitState: (_d = (_c = state2.formStates) == null ? void 0 : _c[formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName)]) == null ? void 0 : _d.submitState
21
29
  };
@@ -79,6 +87,8 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
79
87
  const internalRules = useMemo(() => {
80
88
  return rules || [];
81
89
  }, [rules]);
90
+ useEffect(() => {
91
+ }, [value, listener]);
82
92
  const { data: errors, state } = useTaskEffect({
83
93
  async task() {
84
94
  const listErrors = [];
@@ -258,10 +268,13 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
258
268
  for (const rAsync of ruleToTask) {
259
269
  await rAsync();
260
270
  }
261
- setListener({ formItemId, internalErrors: listErrors });
262
271
  return listErrors;
263
272
  },
264
- deps: [internalRules, value],
273
+ deps: [internalRules, value, listener == null ? void 0 : listener.isInitied],
274
+ enabled: Boolean(listener == null ? void 0 : listener.isInitied),
275
+ onSuccess(result) {
276
+ setListener({ formItemId, internalErrors: result });
277
+ },
265
278
  onError(err) {
266
279
  }
267
280
  });
@@ -278,6 +291,11 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
278
291
  initiedData: true
279
292
  });
280
293
  }
294
+ } else {
295
+ onChange(value, {
296
+ notTriggerDirty: true,
297
+ initiedData: true
298
+ });
281
299
  }
282
300
  return;
283
301
  }
@@ -322,14 +340,16 @@ function useFormItemControl({ formName, form, name, initialValue, formItemId, ru
322
340
  const isIncludeDirectoryInCache = has(cacheData, name);
323
341
  if (!isIncludeDirectoryInCache && isNil(getNewDataFromCache)) {
324
342
  onChange(initialValue, {
325
- notTriggerDirty: true
343
+ notTriggerDirty: true,
344
+ initiedData: true
326
345
  });
327
346
  } else
328
- onChange(getNewDataFromCache, { notTriggerDirty: true });
347
+ onChange(getNewDataFromCache, {
348
+ notTriggerDirty: true,
349
+ initiedData: true
350
+ });
329
351
  }
330
352
  }, [name, formName || (form == null ? void 0 : form.formName) || (contextForm == null ? void 0 : contextForm.formName)]);
331
- useEffect(() => {
332
- }, [submitState]);
333
353
  return {
334
354
  value,
335
355
  onChange,
@@ -240,21 +240,19 @@ const createListenersSlice = (storeSet, storeGet, api) => ({
240
240
  }
241
241
  return;
242
242
  }
243
- if (name && formName) {
244
- storeListeners.push({
245
- name,
246
- formName,
247
- isTouched: Boolean(isTouched),
248
- isDirty: Boolean(isDirty),
249
- formItemId,
250
- internalErrors,
251
- onChange,
252
- onReset,
253
- isInitied: Boolean(isInitied),
254
- type: type || "normal",
255
- onArrayChange
256
- });
257
- }
243
+ storeListeners.push({
244
+ name,
245
+ formName,
246
+ isTouched: Boolean(isTouched),
247
+ isDirty: Boolean(isDirty),
248
+ formItemId,
249
+ internalErrors,
250
+ onChange,
251
+ onReset,
252
+ isInitied: Boolean(isInitied),
253
+ type: type || "normal",
254
+ onArrayChange
255
+ });
258
256
  }));
259
257
  },
260
258
  revokeListener(formItemId, onAfterRevoke) {
@@ -0,0 +1,3 @@
1
+ type Props = {};
2
+ declare function TestDialog({}: Props): import("react/jsx-runtime").JSX.Element;
3
+ export default TestDialog;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Dialog, DialogContent, DialogContentText, DialogTitle, TextField } from "@mui/material";
3
+ import { Box } from "@mui/system";
4
+ import { useToggle } from "minh-custom-hooks-release";
5
+ import FormItem from "../components/Form/FormItem";
6
+ import TestSelect from "./TestSelect";
7
+ function TestDialog({}) {
8
+ const { state: open, toggle } = useToggle();
9
+ return _jsxs(Box, { children: [_jsxs(Dialog, { open, onClose: toggle, children: [_jsx(DialogTitle, { children: "Test Dialog" }), _jsxs(DialogContent, { children: [_jsx(DialogContentText, { children: "This is a test dialog." }), _jsx(FormItem, { rules: [
10
+ {
11
+ handler(value) {
12
+ return Boolean(value);
13
+ },
14
+ message: "Testt"
15
+ }
16
+ ], controlAfterInit: true, initialValue: null, name: "testSelectInsideDialog", children: _jsx(TestSelect, {}) }), _jsx(FormItem, { controlAfterInit: true, initialValue: "", name: "anotherField", children: _jsx(TextField, {}) })] })] }), _jsx(Button, { variant: "contained", onClick: toggle, children: "Open Test Dialog" })] });
17
+ }
18
+ var stdin_default = TestDialog;
19
+ export {
20
+ stdin_default as default
21
+ };
@@ -0,0 +1,6 @@
1
+ type Props = {
2
+ value?: any;
3
+ onChange?: (value: any) => void;
4
+ };
5
+ declare function TestSelect({ value, onChange }: Props): import("react/jsx-runtime").JSX.Element;
6
+ export default TestSelect;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Autocomplete, TextField } from "@mui/material";
3
+ function TestSelect({ value = null, onChange }) {
4
+ return _jsx(Autocomplete, { value, onChange: (_, newValue) => {
5
+ onChange == null ? void 0 : onChange(newValue);
6
+ }, renderInput: (params) => _jsx(TextField, { ...params }), options: [
7
+ {
8
+ value: "option1",
9
+ label: "Option 1"
10
+ },
11
+ {
12
+ value: "option2",
13
+ label: "Option 2"
14
+ }
15
+ ], getOptionKey: (o) => {
16
+ return o == null ? void 0 : o.value;
17
+ }, getOptionLabel: (o) => {
18
+ return o == null ? void 0 : o.label;
19
+ } });
20
+ }
21
+ var stdin_default = TestSelect;
22
+ export {
23
+ stdin_default as default
24
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-form-manage",
3
- "version": "1.0.8-beta.18",
3
+ "version": "1.0.8-beta.20",
4
4
  "description": "Lightweight React form management with list and listener support.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/App.tsx CHANGED
@@ -6,6 +6,7 @@ import FormItem from "./components/Form/FormItem";
6
6
  import FormList from "./components/Form/FormList";
7
7
  import InputWrapper from "./components/Form/InputWrapper";
8
8
  import Form, { useForm } from "./providers/Form";
9
+ import TestDialog from "./test/TestDialog";
9
10
 
10
11
  import { Form as AntdForm } from "antd";
11
12
 
@@ -154,6 +155,7 @@ const App = () => {
154
155
  </div>
155
156
  )}
156
157
  </FormList>
158
+ <TestDialog />
157
159
  <Button
158
160
  onClick={() => {
159
161
  form?.setFieldValue("arr", [
@@ -68,7 +68,7 @@ export default function useFormItemControl<T = any>({
68
68
  setData,
69
69
  getCacheData,
70
70
  getFormValues,
71
- getFormState,
71
+ // getFormState,
72
72
  isStateInitied,
73
73
  submitState,
74
74
  } = useFormStore(
@@ -81,7 +81,7 @@ export default function useFormItemControl<T = any>({
81
81
  setData: state.setData,
82
82
  getCacheData: state.getCacheData,
83
83
  getFormValues: state.getFormValues,
84
- getFormState: state.getFormState,
84
+ // getFormState: state.getFormState,
85
85
  isStateInitied:
86
86
  state.formStates?.[
87
87
  formName || form?.formName || contextForm?.formName
@@ -215,8 +215,17 @@ export default function useFormItemControl<T = any>({
215
215
  return rules || [];
216
216
  }, [rules]);
217
217
 
218
+ useEffect(() => {
219
+ console.log("Rules changed: ", { name, listener });
220
+ }, [value, listener]);
221
+
218
222
  const { data: errors, state } = useTaskEffect({
219
223
  async task() {
224
+ console.log("Trigger validate for form item: ", {
225
+ formName: formName || form?.formName || contextForm?.formName,
226
+ name,
227
+ value,
228
+ });
220
229
  const listErrors = [];
221
230
 
222
231
  const formValues = getFormValues(
@@ -505,11 +514,13 @@ export default function useFormItemControl<T = any>({
505
514
  await rAsync();
506
515
  }
507
516
 
508
- setListener({ formItemId, internalErrors: listErrors });
509
-
510
517
  return listErrors;
511
518
  },
512
- deps: [internalRules, value],
519
+ deps: [internalRules, value, listener?.isInitied],
520
+ enabled: Boolean(listener?.isInitied),
521
+ onSuccess(result) {
522
+ setListener({ formItemId, internalErrors: result });
523
+ },
513
524
  onError(err) {
514
525
  console.log(err);
515
526
  },
@@ -542,6 +553,11 @@ export default function useFormItemControl<T = any>({
542
553
  initiedData: true,
543
554
  });
544
555
  }
556
+ } else {
557
+ onChange(value, {
558
+ notTriggerDirty: true,
559
+ initiedData: true,
560
+ });
545
561
  }
546
562
  return;
547
563
  }
@@ -604,15 +620,16 @@ export default function useFormItemControl<T = any>({
604
620
  if (!isIncludeDirectoryInCache && isNil(getNewDataFromCache)) {
605
621
  onChange(initialValue, {
606
622
  notTriggerDirty: true,
623
+ initiedData: true,
624
+ });
625
+ } else
626
+ onChange(getNewDataFromCache, {
627
+ notTriggerDirty: true,
628
+ initiedData: true,
607
629
  });
608
- } else onChange(getNewDataFromCache, { notTriggerDirty: true });
609
630
  }
610
631
  }, [name, formName || form?.formName || contextForm?.formName]);
611
632
 
612
- useEffect(() => {
613
- console.log("Submit state changed in useFormItemControl: ", submitState);
614
- }, [submitState]);
615
-
616
633
  return {
617
634
  value: value as T,
618
635
  onChange,
@@ -430,21 +430,19 @@ const createListenersSlice = (storeSet: any, storeGet: any, api: any) => ({
430
430
 
431
431
  return;
432
432
  }
433
- if (name && formName) {
434
- storeListeners.push({
435
- name,
436
- formName,
437
- isTouched: Boolean(isTouched),
438
- isDirty: Boolean(isDirty),
439
- formItemId,
440
- internalErrors,
441
- onChange,
442
- onReset,
443
- isInitied: Boolean(isInitied),
444
- type: type || "normal",
445
- onArrayChange,
446
- });
447
- }
433
+ storeListeners.push({
434
+ name,
435
+ formName,
436
+ isTouched: Boolean(isTouched),
437
+ isDirty: Boolean(isDirty),
438
+ formItemId,
439
+ internalErrors,
440
+ onChange,
441
+ onReset,
442
+ isInitied: Boolean(isInitied),
443
+ type: type || "normal",
444
+ onArrayChange,
445
+ });
448
446
  }),
449
447
  );
450
448
  },
@@ -0,0 +1,52 @@
1
+ import {
2
+ Button,
3
+ Dialog,
4
+ DialogContent,
5
+ DialogContentText,
6
+ DialogTitle,
7
+ TextField,
8
+ } from "@mui/material";
9
+ import { Box } from "@mui/system";
10
+ import { useToggle } from "minh-custom-hooks-release";
11
+ import FormItem from "../components/Form/FormItem";
12
+ import TestSelect from "./TestSelect";
13
+
14
+ type Props = {};
15
+
16
+ function TestDialog({}: Props) {
17
+ const { state: open, toggle } = useToggle();
18
+ return (
19
+ <Box>
20
+ <Dialog open={open} onClose={toggle}>
21
+ <DialogTitle>Test Dialog</DialogTitle>
22
+ <DialogContent>
23
+ <DialogContentText>This is a test dialog.</DialogContentText>
24
+ <FormItem
25
+ rules={[
26
+ {
27
+ handler(value) {
28
+ console.log("Validating in dialog: ", value);
29
+ return Boolean(value);
30
+ },
31
+ message: "Testt",
32
+ },
33
+ ]}
34
+ controlAfterInit
35
+ initialValue={null}
36
+ name="testSelectInsideDialog"
37
+ >
38
+ <TestSelect />
39
+ </FormItem>
40
+ <FormItem controlAfterInit initialValue={""} name="anotherField">
41
+ <TextField />
42
+ </FormItem>
43
+ </DialogContent>
44
+ </Dialog>
45
+ <Button variant="contained" onClick={toggle}>
46
+ Open Test Dialog
47
+ </Button>
48
+ </Box>
49
+ );
50
+ }
51
+
52
+ export default TestDialog;
@@ -0,0 +1,38 @@
1
+ import { Autocomplete, TextField } from "@mui/material";
2
+
3
+ type Props = {
4
+ value?: any;
5
+ onChange?: (value: any) => void;
6
+ };
7
+
8
+ function TestSelect({ value = null, onChange }: Props) {
9
+ return (
10
+ <Autocomplete
11
+ value={value}
12
+ onChange={(_, newValue) => {
13
+ onChange?.(newValue);
14
+ }}
15
+ renderInput={(params) => <TextField {...params} />}
16
+ options={[
17
+ {
18
+ value: "option1",
19
+ label: "Option 1",
20
+ },
21
+ {
22
+ value: "option2",
23
+ label: "Option 2",
24
+ },
25
+ ]}
26
+ getOptionKey={(o) => {
27
+ // console.log("Option get key: ", o);
28
+ return o?.value;
29
+ }}
30
+ getOptionLabel={(o) => {
31
+ // console.log("Option get label: ", o);
32
+ return o?.label;
33
+ }}
34
+ />
35
+ );
36
+ }
37
+
38
+ export default TestSelect;