react-form-manage 1.1.0-beta.4 → 1.1.0-beta.6

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,39 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.1.0-beta.6] - 2026-03-18
6
+
7
+ ### Reset Improvements
8
+
9
+ - **Reset submit history**: `resetFields(values?, { resetSubmitData?: boolean })` now defaults `resetSubmitData` to `true` and clears previous submit data when resetting.
10
+ - **Store cleanup**: Added `clearSubmitHistory` to ensure submit history is cleared when resetting/clearing the form.
11
+
12
+ ### Type Improvements
13
+
14
+ ## [1.1.0-beta.5] - 2026-03-09
15
+
16
+ ### Type Improvements
17
+
18
+ - **Enhanced Submit Methods**: Updated `submit` and `submitAsync` method signatures
19
+ - Changed from `submit(values?: T)` to `submit(props?: SubmitProps<T>)`
20
+ - Changed from `submitAsync(values?: T)` to `submitAsync(props?: SubmitProps<T>)`
21
+ - Now supports external callback functions for better submit flow control
22
+
23
+ ### New Features
24
+
25
+ - **SubmitProps Interface**: New interface for submit method parameters
26
+ - `externalFinishCallback`: Called when form validation succeeds with form values
27
+ - `externalRejectCallback`: Called when form validation fails with error fields
28
+ - `externalFinallyCallback`: Called after submit completes with complete result info
29
+ - All callbacks support both synchronous and asynchronous execution
30
+
31
+ ### Technical Details
32
+
33
+ - `SubmitProps<T>` interface exported from formStore
34
+ - Allows programmatic submit with custom callbacks without requiring Form component props
35
+ - Better separation of concerns between form state and submit handlers
36
+ - Enables more flexible submit workflows (e.g., submit via form instance methods)
37
+
5
38
  ## [1.1.0-beta.4] - 2026-03-04
6
39
 
7
40
  ### Performance & Code Quality
@@ -27,7 +27,8 @@ const InternalForm = function Form({ children, formName: externalFormName, form:
27
27
  setSubmitHistory,
28
28
  clearFormValues,
29
29
  clearFormInitialValues,
30
- clearFormState
30
+ clearFormState,
31
+ clearSubmitHistory
31
32
  } = useFormStore(useShallow((state) => ({
32
33
  // appInitValue: state.initialValues,
33
34
  setInitData: state.setInitData,
@@ -40,7 +41,8 @@ const InternalForm = function Form({ children, formName: externalFormName, form:
40
41
  getFormItemValue: state.getFormItemValue,
41
42
  clearFormValues: state.clearFormValues,
42
43
  clearFormInitialValues: state.clearFormInitialValues,
43
- clearFormState: state.clearFormState
44
+ clearFormState: state.clearFormState,
45
+ clearSubmitHistory: state.clearSubmitHistory
44
46
  // Test, nhớ xóa sau khi xong
45
47
  // formStates: state.formStates?.[formName],
46
48
  })));
@@ -339,7 +341,7 @@ const InternalForm = function Form({ children, formName: externalFormName, form:
339
341
  }
340
342
  }
341
343
  });
342
- const resetFields = (resetOptions) => {
344
+ const resetFields = (resetValues, resetOptions = { resetSubmitData: true }) => {
343
345
  reset();
344
346
  flushSync(setFormState({
345
347
  formName,
@@ -347,25 +349,25 @@ const InternalForm = function Form({ children, formName: externalFormName, form:
347
349
  submitState: SUBMIT_STATE.IDLE
348
350
  }));
349
351
  const totalListenerFields = getListeners();
350
- if (Array.isArray(resetOptions)) {
352
+ if (Array.isArray(resetValues)) {
351
353
  totalListenerFields.forEach((l) => {
352
- const findListenerByName = resetOptions.find((o) => o.name === l.name);
354
+ const findListenerByName = resetValues.find((o) => o.name === l.name);
353
355
  if (findListenerByName) {
354
356
  l.onReset(findListenerByName == null ? void 0 : findListenerByName.value);
355
357
  }
356
358
  });
357
- totalListenerFields.filter((l) => resetOptions.find((o) => o.name !== l.name)).forEach((l) => {
359
+ totalListenerFields.filter((l) => resetValues.find((o) => o.name !== l.name)).forEach((l) => {
358
360
  var _a;
359
361
  (_a = l == null ? void 0 : l.onReset) == null ? void 0 : _a.call(l);
360
362
  });
361
- } else if (typeof resetOptions === "object") {
362
- const allStringPath = getAllNoneObjStringPath(resetOptions);
363
+ } else if (typeof resetValues === "object") {
364
+ const allStringPath = getAllNoneObjStringPath(resetValues);
363
365
  allStringPath.forEach((p) => {
364
366
  const listener = totalListenerFields.find((l) => l.name === p && l.formName === formName);
365
367
  if (listener) {
366
- listener.onChange(get(resetOptions, listener.name));
368
+ listener.onChange(get(resetValues, listener.name));
367
369
  } else {
368
- setData(formName, p, get(resetOptions, p));
370
+ setData(formName, p, get(resetValues, p));
369
371
  }
370
372
  });
371
373
  } else {
@@ -374,6 +376,9 @@ const InternalForm = function Form({ children, formName: externalFormName, form:
374
376
  (_a = l == null ? void 0 : l.onReset) == null ? void 0 : _a.call(l);
375
377
  });
376
378
  }
379
+ if (resetOptions == null ? void 0 : resetOptions.resetSubmitData) {
380
+ setSubmitHistory(formName, {});
381
+ }
377
382
  setFormState({
378
383
  formName,
379
384
  isInitied: true
@@ -410,6 +415,7 @@ const InternalForm = function Form({ children, formName: externalFormName, form:
410
415
  clearFormInitialValues(formName);
411
416
  clearFormValues(formName);
412
417
  clearFormState(formName);
418
+ clearSubmitHistory(formName);
413
419
  };
414
420
  }, []);
415
421
  return _jsx(FormContext.Provider, { value: {
@@ -206,6 +206,12 @@ const createFormStoreSlice = (storeSet, storeGet, api) => ({
206
206
  set(state.submitHistory, formName, [submitData]);
207
207
  }
208
208
  }));
209
+ },
210
+ clearSubmitHistory(formName) {
211
+ return storeSet(produce((state) => {
212
+ const oldValues = state.submitHistory;
213
+ unset(oldValues, formName);
214
+ }));
209
215
  }
210
216
  });
211
217
  const createListenersSlice = (storeSet, storeGet, api) => ({
@@ -0,0 +1,3 @@
1
+ type Props = {};
2
+ declare function TestModalReRenderUI({}: Props): import("react/jsx-runtime").JSX.Element;
3
+ export default TestModalReRenderUI;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Dialog, DialogContent } from "@mui/material";
3
+ import { Button } from "antd";
4
+ import { useToggle } from "minh-custom-hooks-release";
5
+ import TestMuiReRenderForm from "./TestMuiReRenderForm";
6
+ function TestModalReRenderUI({}) {
7
+ const { state, on, off } = useToggle();
8
+ return _jsxs("div", { children: [_jsx(Button, { onClick: on, children: "Open Modal" }), _jsx(Dialog, { open: state, onClose: off, children: _jsx(DialogContent, { children: _jsx(TestMuiReRenderForm, {}) }) })] });
9
+ }
10
+ var stdin_default = TestModalReRenderUI;
11
+ export {
12
+ stdin_default as default
13
+ };
@@ -0,0 +1,3 @@
1
+ type Props = {};
2
+ declare function TestMuiReRenderForm({}: Props): import("react/jsx-runtime").JSX.Element;
3
+ export default TestMuiReRenderForm;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TextField } from "@mui/material";
3
+ import { useEffect } from "react";
4
+ import Form from "../../providers/Form";
5
+ function TestMuiReRenderForm({}) {
6
+ const [form, isMounted] = Form.useForm("testMuiReRender");
7
+ const name = Form.useWatch("name", form);
8
+ useEffect(() => {
9
+ if (isMounted) {
10
+ form.setFieldValue("name", "John Doe");
11
+ }
12
+ }, [isMounted]);
13
+ useEffect(() => {
14
+ if (name) {
15
+ setTimeout(() => {
16
+ form.setFieldValue("name2", "Hello " + name);
17
+ }, 1e3);
18
+ }
19
+ }, [name]);
20
+ return _jsxs(Form, { formName: "testMuiReRender", children: [_jsx(Form.Item, { controlAfterInit: true, initialValue: "", name: "name", children: _jsx(TextField, { label: "Name", disabled: true }) }), _jsx(Form.Item, { controlAfterInit: true, initialValue: "", name: "name2", children: _jsx(TextField, { label: "Greeting", disabled: true }) })] });
21
+ }
22
+ var stdin_default = TestMuiReRenderForm;
23
+ export {
24
+ stdin_default as default
25
+ };
@@ -0,0 +1,3 @@
1
+ type Props = {};
2
+ declare function TestMuiReRender({}: Props): import("react/jsx-runtime").JSX.Element;
3
+ export default TestMuiReRender;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import TestModalReRenderUI from "./TestModalReRenderUI";
3
+ function TestMuiReRender({}) {
4
+ return _jsx(TestModalReRenderUI, {});
5
+ }
6
+ var stdin_default = TestMuiReRender;
7
+ export {
8
+ stdin_default as default
9
+ };
@@ -1,6 +1,7 @@
1
1
  import type { SUBMIT_STATE } from "../constants/form";
2
2
  import { OnChangeOptions } from "../hooks/useFormItemControl";
3
3
  import { SetFieldValueOptions } from "../providers/Form";
4
+ import { SubmitProps } from "../stores/formStore";
4
5
  import type { GetConstantType } from "./util";
5
6
  export type FormValues<T = any> = T;
6
7
  export interface FormFieldError {
@@ -33,9 +34,11 @@ export interface ValidationRule<T = any, TValues = any> {
33
34
  }
34
35
  export interface PublicFormInstance<T = any> {
35
36
  formName: string;
36
- resetFields: (values?: Partial<T>) => void;
37
- submit: (values?: T) => void;
38
- submitAsync: (values?: T) => Promise<void>;
37
+ resetFields: (values?: Partial<T>, options?: {
38
+ resetSubmitData?: boolean;
39
+ }) => void;
40
+ submit: (props?: SubmitProps<T>) => void;
41
+ submitAsync: (props?: SubmitProps<T>) => Promise<void>;
39
42
  setFieldValue: (name: keyof T | (string & {}), value: any, options?: SetFieldValueOptions) => void;
40
43
  setFieldValues: (values: Partial<T>, options?: OnChangeOptions) => void;
41
44
  getFieldValue: (name: keyof T | (string & {})) => any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-form-manage",
3
- "version": "1.1.0-beta.4",
3
+ "version": "1.1.0-beta.6",
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
@@ -1,7 +1,7 @@
1
1
  import Form from "./providers/Form";
2
2
 
3
3
  import { Form as AntdForm } from "antd";
4
- import SelfTestGetForm from "./test/SelfTestGetForm";
4
+ import TestMuiReRender from "./test/TestMuiReRender";
5
5
 
6
6
  function TestFormWatch() {
7
7
  const watchValue = Form.useWatch("numericCode");
@@ -48,7 +48,9 @@ const App = () => {
48
48
 
49
49
  {/* <FormInstanceTestSuite /> */}
50
50
 
51
- <SelfTestGetForm />
51
+ {/* <SelfTestGetForm /> */}
52
+
53
+ <TestMuiReRender />
52
54
  </div>
53
55
  );
54
56
  };
@@ -107,6 +107,7 @@ const InternalForm: FormDeclearation = function Form<T = any>({
107
107
  clearFormValues,
108
108
  clearFormInitialValues,
109
109
  clearFormState,
110
+ clearSubmitHistory,
110
111
  } = useFormStore(
111
112
  useShallow((state) => ({
112
113
  // appInitValue: state.initialValues,
@@ -121,7 +122,7 @@ const InternalForm: FormDeclearation = function Form<T = any>({
121
122
  clearFormValues: state.clearFormValues,
122
123
  clearFormInitialValues: state.clearFormInitialValues,
123
124
  clearFormState: state.clearFormState,
124
-
125
+ clearSubmitHistory: state.clearSubmitHistory,
125
126
  // Test, nhớ xóa sau khi xong
126
127
  // formStates: state.formStates?.[formName],
127
128
  })),
@@ -699,7 +700,10 @@ const InternalForm: FormDeclearation = function Form<T = any>({
699
700
  },
700
701
  });
701
702
 
702
- const resetFields = (resetOptions) => {
703
+ const resetFields = (
704
+ resetValues: any,
705
+ resetOptions = { resetSubmitData: true },
706
+ ) => {
703
707
  reset();
704
708
  flushSync(
705
709
  setFormState({
@@ -709,9 +713,9 @@ const InternalForm: FormDeclearation = function Form<T = any>({
709
713
  }),
710
714
  );
711
715
  const totalListenerFields = getListeners();
712
- if (Array.isArray(resetOptions)) {
716
+ if (Array.isArray(resetValues)) {
713
717
  totalListenerFields.forEach((l) => {
714
- const findListenerByName = resetOptions.find((o) => o.name === l.name);
718
+ const findListenerByName = resetValues.find((o) => o.name === l.name);
715
719
 
716
720
  if (findListenerByName) {
717
721
  l.onReset(findListenerByName?.value);
@@ -719,20 +723,20 @@ const InternalForm: FormDeclearation = function Form<T = any>({
719
723
  });
720
724
 
721
725
  totalListenerFields
722
- .filter((l) => resetOptions.find((o) => o.name !== l.name))
726
+ .filter((l) => resetValues.find((o) => o.name !== l.name))
723
727
  .forEach((l) => {
724
728
  l?.onReset?.();
725
729
  });
726
- } else if (typeof resetOptions === "object") {
727
- const allStringPath = getAllNoneObjStringPath(resetOptions);
730
+ } else if (typeof resetValues === "object") {
731
+ const allStringPath = getAllNoneObjStringPath(resetValues);
728
732
  allStringPath.forEach((p) => {
729
733
  const listener = totalListenerFields.find(
730
734
  (l) => l.name === p && l.formName === formName,
731
735
  );
732
736
  if (listener) {
733
- listener.onChange(get(resetOptions, listener.name));
737
+ listener.onChange(get(resetValues, listener.name));
734
738
  } else {
735
- setData(formName, p, get(resetOptions, p));
739
+ setData(formName, p, get(resetValues, p));
736
740
  }
737
741
  });
738
742
  } else {
@@ -741,6 +745,10 @@ const InternalForm: FormDeclearation = function Form<T = any>({
741
745
  });
742
746
  }
743
747
 
748
+ if (resetOptions?.resetSubmitData) {
749
+ setSubmitHistory(formName, {} as T);
750
+ }
751
+
744
752
  setFormState({
745
753
  formName,
746
754
  isInitied: true,
@@ -761,6 +769,7 @@ const InternalForm: FormDeclearation = function Form<T = any>({
761
769
  setInitData(formName, p, get(initialValues, p));
762
770
  });
763
771
  }
772
+
764
773
  setFormState({
765
774
  formName,
766
775
  isInitied: true,
@@ -788,6 +797,7 @@ const InternalForm: FormDeclearation = function Form<T = any>({
788
797
  clearFormInitialValues(formName);
789
798
  clearFormValues(formName);
790
799
  clearFormState(formName);
800
+ clearSubmitHistory(formName);
791
801
  };
792
802
  }, []);
793
803
 
@@ -401,6 +401,14 @@ const createFormStoreSlice = (storeSet: any, storeGet: any, api: any) => ({
401
401
  }),
402
402
  );
403
403
  },
404
+ clearSubmitHistory(formName) {
405
+ return storeSet(
406
+ produce<any>((state: any) => {
407
+ const oldValues = state.submitHistory;
408
+ unset(oldValues, formName);
409
+ }),
410
+ );
411
+ },
404
412
  });
405
413
 
406
414
  // Listeners Slice
@@ -0,0 +1,22 @@
1
+ import { Dialog, DialogContent } from "@mui/material";
2
+ import { Button } from "antd";
3
+ import { useToggle } from "minh-custom-hooks-release";
4
+ import TestMuiReRenderForm from "./TestMuiReRenderForm";
5
+
6
+ type Props = {};
7
+
8
+ function TestModalReRenderUI({}: Props) {
9
+ const { state, on, off } = useToggle();
10
+ return (
11
+ <div>
12
+ <Button onClick={on}>Open Modal</Button>
13
+ <Dialog open={state} onClose={off}>
14
+ <DialogContent>
15
+ <TestMuiReRenderForm />
16
+ </DialogContent>
17
+ </Dialog>
18
+ </div>
19
+ );
20
+ }
21
+
22
+ export default TestModalReRenderUI;
@@ -0,0 +1,37 @@
1
+ import { TextField } from "@mui/material";
2
+ import { useEffect } from "react";
3
+ import Form from "../../providers/Form";
4
+
5
+ type Props = {};
6
+
7
+ function TestMuiReRenderForm({}: Props) {
8
+ const [form, isMounted] = Form.useForm("testMuiReRender");
9
+ const name = Form.useWatch("name", form);
10
+
11
+ useEffect(() => {
12
+ if (isMounted) {
13
+ form.setFieldValue("name", "John Doe");
14
+ }
15
+ }, [isMounted]);
16
+
17
+ useEffect(() => {
18
+ if (name) {
19
+ setTimeout(() => {
20
+ form.setFieldValue("name2", "Hello " + name);
21
+ }, 1000);
22
+ }
23
+ }, [name]);
24
+ return (
25
+ <Form formName="testMuiReRender">
26
+ <Form.Item controlAfterInit initialValue={""} name="name">
27
+ <TextField label="Name" disabled />
28
+ </Form.Item>
29
+
30
+ <Form.Item controlAfterInit initialValue={""} name="name2">
31
+ <TextField label="Greeting" disabled />
32
+ </Form.Item>
33
+ </Form>
34
+ );
35
+ }
36
+
37
+ export default TestMuiReRenderForm;
@@ -0,0 +1,9 @@
1
+ import TestModalReRenderUI from "./TestModalReRenderUI";
2
+
3
+ type Props = {};
4
+
5
+ function TestMuiReRender({}: Props) {
6
+ return <TestModalReRenderUI />;
7
+ }
8
+
9
+ export default TestMuiReRender;
@@ -1,6 +1,7 @@
1
1
  import type { SUBMIT_STATE } from "../constants/form";
2
2
  import { OnChangeOptions } from "../hooks/useFormItemControl";
3
3
  import { SetFieldValueOptions } from "../providers/Form";
4
+ import { SubmitProps } from "../stores/formStore";
4
5
  import type { GetConstantType } from "./util";
5
6
 
6
7
  export type FormValues<T = any> = T;
@@ -37,9 +38,12 @@ export interface ValidationRule<T = any, TValues = any> {
37
38
 
38
39
  export interface PublicFormInstance<T = any> {
39
40
  formName: string;
40
- resetFields: (values?: Partial<T>) => void;
41
- submit: (values?: T) => void;
42
- submitAsync: (values?: T) => Promise<void>;
41
+ resetFields: (
42
+ values?: Partial<T>,
43
+ options?: { resetSubmitData?: boolean },
44
+ ) => void;
45
+ submit: (props?: SubmitProps<T>) => void;
46
+ submitAsync: (props?: SubmitProps<T>) => Promise<void>;
43
47
  setFieldValue: (
44
48
  name: keyof T | (string & {}),
45
49
  value: any,
@@ -1 +0,0 @@
1
- export * from "./index";
@@ -1 +0,0 @@
1
- export * from "./index";