UNPKG

@imtf/rjsf-conditionals

Version:

Extension of @rjsf/core with conditional field support

79 lines (70 loc) 2.6 kB
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 = (formData, schema, uiSchema, rules, handleChange, Engine, extraActions = {}) => { // 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(({ formData: nextFormData, ...nextSchemas }) => 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;