diff --git a/README.md b/README.md
index dd41bd0..d18dcd4 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,7 @@ It also exports the following functions:
- **`initFormuleSchema`**: Inits or resets the JSONSchema. You can also load an existing schema by passing it as an argument.
- **`getFormuleState`**: Formule has its own internal redux state. You can retrieve it at any moment if you so require for more advanced use cases. If you want to continuosly synchronize the Formule state in your app, you can pass a callback function to FormuleContext instead (see below), which will be called every time the form state changes.
+- **`getFormState`**: Formule also has a separate state for the form data, which stores the data input into each of the fields of the form. You can also synchronize this state, but keep in mind that doing it can cause performance issues with big forms.
And the following utilities:
@@ -172,7 +173,7 @@ const transformErrors = (errors) => {
```
-### Syncing Formule state
+### Syncing Formule / Form state
If you want to run some logic in your application every time the current Formule state changes in any way (e.g. to run some action every time a new field is added to the form) you can pass a function to be called back when that happens:
@@ -181,13 +182,15 @@ const handleFormuleStateChange = (newState) => {
// Do something when the state changes
};
-
+
// ...
;
```
Alternatively, you can pull the current state on demand by calling `getFormuleState` at any moment.
+You can also synchronize the form state (e.g. `formData`) by provinding a callback via `syncFormState`, but beware of the performance impact on big forms and prefer calling `getFormState` instead when needed.
+
### Loading form data / prefill form
If you want to prefill the form with existing data, you can provide the form data to `FormuleForm`. This will fill in the corresponding fields with the information in `formData`:
diff --git a/formule-demo/src/App.tsx b/formule-demo/src/App.tsx
index af0612d..1464785 100644
--- a/formule-demo/src/App.tsx
+++ b/formule-demo/src/App.tsx
@@ -47,6 +47,7 @@ import {
saveToLocalStorage,
loadFromLocalStorage,
AiChatFooter,
+ getFormState,
} from "react-formule";
import { theme } from "./theme";
import formuleLogo from "./assets/logo.png";
@@ -127,7 +128,7 @@ const App = () => {
};
return (
-
+
{
value={JSON.stringify(
formuleState?.current.uiSchema,
null,
- 2
+ 2,
)}
lang="json"
height="25vh"
@@ -366,7 +367,7 @@ const App = () => {
>
Form data
{
const reader = new FileReader();
reader.onload = (event) => {
const newSchema = JSON.parse(
- event?.target?.result as string
+ event?.target?.result as string,
);
const { schema, uiSchema } = newSchema;
if (schema && uiSchema) {
@@ -443,7 +444,7 @@ const App = () => {
message.success("Uploaded and loaded successfully");
} else {
message.error(
- "Your json should include a schema and a uiSchema key"
+ "Your json should include a schema and a uiSchema key",
);
}
};
diff --git a/src/StateSynchronizer.jsx b/src/StateSynchronizer.jsx
index 9af3353..602c216 100644
--- a/src/StateSynchronizer.jsx
+++ b/src/StateSynchronizer.jsx
@@ -1,12 +1,17 @@
-import { useEffect } from "react";
+import { useEffect, useRef } from "react";
import { useSelector } from "react-redux";
+import { isEqual } from "lodash-es";
-const StateSynchronizer = ({ synchronizeState, children }) => {
- const state = useSelector((state) => state.schemaWizard);
+const StateSynchronizer = ({ callback, slice = "schemaWizard", children }) => {
+ const state = useSelector((state) => state[slice]);
+ const previousStateRef = useRef(state);
useEffect(() => {
- synchronizeState(state);
- }, [state, synchronizeState]);
+ if (!isEqual(previousStateRef.current, state)) {
+ callback(state);
+ previousStateRef.current = state;
+ }
+ }, [state, callback]);
return children;
};
diff --git a/src/admin/components/EditablePreview.jsx b/src/admin/components/EditablePreview.jsx
index a886db6..83f43cd 100644
--- a/src/admin/components/EditablePreview.jsx
+++ b/src/admin/components/EditablePreview.jsx
@@ -1,19 +1,29 @@
-import { useContext } from "react";
+import React, { useMemo } from "react";
+import { useDispatch } from "react-redux";
+import { debounce } from "lodash-es";
import Form from "../../forms/Form";
import { shoudDisplayGuideLinePopUp } from "../utils";
import { Row, Empty, Space, Typography } from "antd";
-import { useDispatch, useSelector } from "react-redux";
-import CustomizationContext from "../../contexts/CustomizationContext";
-import { updateFormData } from "../../store/schemaWizard";
-
-const EditablePreview = ({ hideTitle, liveValidate }) => {
- const schema = useSelector((state) => state.schemaWizard.current.schema);
- const uiSchema = useSelector((state) => state.schemaWizard.current.uiSchema);
- const formData = useSelector((state) => state.schemaWizard.formData);
+import { updateFormData } from "../../store/form";
+const EditablePreview = ({
+ hideTitle,
+ liveValidate,
+ schema,
+ uiSchema,
+ formData,
+}) => {
const dispatch = useDispatch();
- const customizationContext = useContext(CustomizationContext);
+ const debouncedDispatch = useMemo(() => {
+ return debounce((newFormData) => {
+ dispatch(updateFormData({ value: newFormData }));
+ }, 500);
+ }, [dispatch]);
+
+ const handleFormChange = (change) => {
+ debouncedDispatch(change.formData);
+ };
return (
<>
@@ -47,12 +57,10 @@ const EditablePreview = ({ hideTitle, liveValidate }) => {
) : (