Skip to content

Commit

Permalink
fix: all rendering-related issues on FormComponentSlots, SimpleFormRe…
Browse files Browse the repository at this point in the history
…nderer, and FormRenderer
  • Loading branch information
nedpals committed Nov 25, 2024
1 parent 955acbb commit e24fb6d
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 179 deletions.
35 changes: 12 additions & 23 deletions lib/components/FormComponentSlots.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useMemo } from "react";
import { memo } from "react";
import {
FormFieldRenderer,
FormFieldRendererProps,
Expand Down Expand Up @@ -31,12 +31,12 @@ export interface FormComponentSlotProps {

const FormComponentSlots = memo(
({
byType: _byType = {},
byFormType: _byFormType = {},
byProperty: _byProperty = {},
byCustomType: _byCustomType = {},
byCustomControlType: _byCustomControlType = {},
byLayoutName: _byLayoutName = {},
byType = {},
byFormType = {},
byProperty = {},
byCustomType = {},
byCustomControlType = {},
byLayoutName = {},
...props
}: FormFieldRendererProps & FormComponentSlotProps) => {
const {
Expand All @@ -45,17 +45,6 @@ const FormComponentSlots = memo(
preferSchemaTypeComponent,
} = props.preference;

// Memoize all components
const byType = useMemo(() => _byType, [_byType]);
const byFormType = useMemo(() => _byFormType, [_byFormType]);
const byProperty = useMemo(() => _byProperty, [_byProperty]);
const byCustomType = useMemo(() => _byCustomType, [_byCustomType]);
const byCustomControlType = useMemo(
() => _byCustomControlType,
[_byCustomControlType],
);
const byLayoutName = useMemo(() => _byLayoutName, [_byLayoutName]);

// Hierarchical order of precedence for component lookup:
// 1. formComponentsByProperty
// 2. formComponentsByFormType
Expand All @@ -82,15 +71,15 @@ const FormComponentSlots = memo(
const customType = props.formProperties.customContentType;
if (byCustomType[customType]) {
const FormComponent = byCustomType[customType] as FormFieldRenderer;
return <FormComponent {...props} />;
return FormComponent(props);
}
} else if (formType === "custom-control") {
const controlType = props.formProperties.controlType;
if (byCustomControlType[controlType]) {
const FormComponent = byCustomControlType[
controlType
] as FormFieldRenderer;
return <FormComponent {...props} />;
return FormComponent(props);
}
} else if (
formType === "layout" &&
Expand All @@ -100,10 +89,10 @@ const FormComponentSlots = memo(
const FormComponent = byLayoutName[
props.formProperties.name
] as FormFieldRenderer;
return <FormComponent {...props} />;
return FormComponent(props);
} else if (byFormType[formType]) {
const FormComponent = byFormType[formType] as FormFieldRenderer;
return <FormComponent {...props} />;
return FormComponent(props);
}
} else if (preferSchemaTypeComponent) {
const typesList = Array.isArray(props.schema.type)
Expand All @@ -113,7 +102,7 @@ const FormComponentSlots = memo(
for (const type of typesList) {
if (byType[type]) {
const FormComponent = byType[type] as FormFieldRenderer;
return <FormComponent {...props} />;
return FormComponent(props);
}
}
}
Expand Down
134 changes: 67 additions & 67 deletions lib/components/FormRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getProperty } from "dot-prop";
import { FC, useCallback, useMemo } from "react";
import { FC, Fragment, memo, useCallback, useMemo } from "react";
import { JSONSchemaForm } from "../jsf";
import { expandSectionSelector } from "../json_schema";
import { FormRenderContext, useFormRenderContext } from "../render_context";
Expand All @@ -9,7 +9,7 @@ import {
OutletRendererProps,
} from "../form_types";

function FormRendererChild<
const FormRendererChild = <
RootSchemaType extends JSONSchemaForm,
SchemaType extends JSONSchemaForm,
>({
Expand All @@ -19,12 +19,9 @@ function FormRendererChild<
preferFormTypeComponent = true,
preferPropertyComponent = true,
preferSchemaTypeComponent = true,
}: FormRendererProps<SchemaType>) {
const { rootSchema, render: RenderComponent } = useFormRenderContext<
RootSchemaType,
SchemaType
>();

}: FormRendererProps<SchemaType>) => {
const { rootSchema, render: RenderComponent } =
useFormRenderContext<RootSchemaType>();
const _FormRendererChild = useCallback<FC<OutletRendererProps>>(
({
parentProperty: _parentProperty,
Expand All @@ -33,19 +30,17 @@ function FormRendererChild<
preferFormTypeComponent,
preferPropertyComponent,
preferSchemaTypeComponent,
}) => {
return (
<FormRendererChild
schema={_schema ?? schema}
parentProperty={_parentProperty ?? parentProperty}
property={_property ?? property}
// False by default to avoid infinite recursion. You must explicitly set it to true.
preferFormTypeComponent={preferFormTypeComponent ?? false}
preferPropertyComponent={preferPropertyComponent ?? false}
preferSchemaTypeComponent={preferSchemaTypeComponent ?? false}
/>
);
},
}) => (
<FormRendererChild
schema={_schema ?? schema}
parentProperty={_parentProperty ?? parentProperty}
property={_property ?? property}
// False by default to avoid infinite recursion. You must explicitly set it to true.
preferFormTypeComponent={preferFormTypeComponent ?? false}
preferPropertyComponent={preferPropertyComponent ?? false}
preferSchemaTypeComponent={preferSchemaTypeComponent ?? false}
/>
),
[schema, parentProperty, property],
);

Expand Down Expand Up @@ -74,57 +69,62 @@ function FormRendererChild<
}

return (
<RenderComponent
rootSchema={rootSchema}
schema={schema}
Outlet={_FormRendererChild}
fullProperty={fullProperty}
property={property}
formProperties={schema.formProperties}
preference={preference}
/>
<Fragment>
{RenderComponent({
rootSchema: rootSchema,
schema: schema,
Outlet: _FormRendererChild,
fullProperty: fullProperty,
property: property,
formProperties: schema.formProperties,
preference: preference,
})}
</Fragment>
);
}

export default function FormRenderer<SchemaType extends JSONSchemaForm>({
className,
render,
section,
schema,
}: Omit<FormRendererProps<SchemaType>, "property" | "parentProperty"> & {
section?: string;
render: FormFieldRenderer;
className?: string;
}) {
const _render = useCallback(render, []);
};
FormRendererChild.displayName = "FormRendererChild";

const expandedSectionSelector = useMemo(
() => (section ? expandSectionSelector(schema, section) : undefined),
[schema, section],
);
const FormRenderer = memo(
<SchemaType extends JSONSchemaForm>({
className,
render,
section,
schema,
}: Omit<FormRendererProps<SchemaType>, "property" | "parentProperty"> & {
section?: string;
render: FormFieldRenderer;
className?: string;
}) => {
const expandedSectionSelector = useMemo(
() => (section ? expandSectionSelector(schema, section) : undefined),
[schema, section],
);

const selectedSchema = useMemo(
() =>
expandedSectionSelector
? getProperty(schema, expandedSectionSelector)!
: schema,
[schema, expandedSectionSelector],
);
const selectedSchema = useMemo(
() =>
expandedSectionSelector
? getProperty(schema, expandedSectionSelector)!
: schema,
[schema, expandedSectionSelector],
);

return (
<FormRenderContext rootSchema={selectedSchema} render={_render}>
return (
<div className={className}>
<FormRendererChild
schema={selectedSchema}
property={section ?? ""}
parentProperty=""
preferPropertyComponent
preferFormTypeComponent
preferSchemaTypeComponent
/>
<FormRenderContext rootSchema={selectedSchema} render={render}>
<FormRendererChild
schema={selectedSchema}
property={section ?? ""}
parentProperty=""
preferPropertyComponent
preferFormTypeComponent
preferSchemaTypeComponent
/>
</FormRenderContext>
</div>
</FormRenderContext>
);
}
);
},
);

FormRenderer.displayName = "FormRenderer";

export default FormRenderer;
7 changes: 5 additions & 2 deletions lib/render_context.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContext, useContext } from "react";
import { createContext, useContext, useMemo } from "react";
import { JSONSchemaForm } from "./jsf";
import { FormFieldRenderer } from "./form_types";

Expand Down Expand Up @@ -29,16 +29,19 @@ export const useFormRenderContext = <

export function FormRenderContext<RS extends JSONSchemaForm = JSONSchemaForm>({
children,
rootSchema,
rootSchema: _rootSchema,
render,
}: {
children: React.ReactNode;
rootSchema: RS;
render: FormFieldRenderer;
}) {
const rootSchema = useMemo(() => _rootSchema, [_rootSchema]);
return (
<_FormRenderContext.Provider value={{ rootSchema, render }}>
{children}
</_FormRenderContext.Provider>
);
}

FormRenderContext.displayName = "FormRenderContext";
Loading

0 comments on commit e24fb6d

Please sign in to comment.