@imtf/rjsf-conditionals
Version:
Extension of @rjsf/core with conditional field support
83 lines (74 loc) • 2.76 kB
JavaScript
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import rulesRunner from "./rulesRunner";
import { deepEquals } from "@rjsf/utils";
import { unwrapFormData, wrapFormData } from "./utils";
/**
* Usage:
* const { schema, uiSchema } = useFormWithConditionnals(formData, schema, uiSchema, rules, onDataChange, Engine, extraActions)
* return { schema, uiSchema }
* @param formData
* @param schema
* @param uiSchema
* @param rules
* @param handleChange
* @param Engine
* @param [extraActions]
* @return <object>
*/
const useFormWithConditionnals = function (formData, schema, uiSchema, rules, handleChange, Engine) {
let extraActions = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
// Default schema and uiSchema to undefined when falsy
schema = schema || undefined;
uiSchema = uiSchema || undefined;
// Create the rules runner
const runRules = useMemo(() => rulesRunner(schema, uiSchema, rules, Engine, extraActions), [Engine, extraActions, rules, schema, uiSchema]);
// Keep a ref of the last run data
const lastRunFormData = useRef();
// Keep a ref of current formData so we avoid redefining updateState
const currentFormData = useRef();
currentFormData.current = formData;
// Schemas will be defined by the rules
const [state, setState] = useState({
schema: {},
uiSchema: {}
});
const updateState = useCallback(_ref => {
let {
formData: nextFormData,
...nextSchemas
} = _ref;
return setState(state => {
const unwrappedNextFormData = unwrapFormData(nextFormData);
// Did the rules made some changes to the data?
const dataChangedByRules = !deepEquals(unwrappedNextFormData, currentFormData.current);
// trigger onChange only if data changed because of the rules
if (dataChangedByRules) {
handleChange?.({
formData: unwrappedNextFormData
});
}
const schemasChanged = !deepEquals({
schema: state.schema,
uiSchema: state.uiSchema
}, nextSchemas);
if (schemasChanged) {
return nextSchemas;
}
// If the schemas didn't change, we don't need to update the state
return state;
});
}, [handleChange]);
useEffect(() => {
// Did data changes from last run ?
const dataChanged = !deepEquals(lastRunFormData.current, formData);
if (dataChanged) {
// First wrap the formData to prefix field names with entity
const wrappedData = wrapFormData(formData);
runRules(wrappedData).then(updateState);
// Update lastRunFormData reference
lastRunFormData.current = formData;
}
}, [formData, runRules, updateState]);
return state;
};
export default useFormWithConditionnals;