Skip to content

Commit

Permalink
Remove memoization comparison in React bindings
Browse files Browse the repository at this point in the history
Due to previous refactorings data instance references only
change on update when their actual content changed. Therefore
the manual memoization comparison functions ("areEqual")
involving deep equals are no longer necessary.

Other props which changed with each rerendering, like the
enum "options" objects or the array dispatch methods are now
also properly memoized.

Includes minor additional changes like refactoring all
remaining React Material renderers to functional compoonents,
generalizing the React bindings and memoizing intermediate
components.
  • Loading branch information
eneufeld authored and sdirix committed Oct 14, 2021
1 parent 3f706fe commit 8eeae86
Show file tree
Hide file tree
Showing 37 changed files with 996 additions and 1,044 deletions.
7 changes: 7 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## Migrating to JSON Forms 3.0 for React users

### Removal of Class Components in React Material

With Version 3.0 of JSON Forms we removed all React Material class components.
Please check whether you extended any of our base renderers in your adaptation.

### Removal of JSON Schema $Ref Parser

With version 3.0 of JSON Forms, we removed the `json-schema-ref-parser` dependency within the core package.
This change only affects users of the React variant, Vue and Angular users are not affected.

Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/util/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1002,8 +1002,6 @@ export const mapStateToArrayLayoutProps = (
};
};

export type CombinatorProps = StatePropsOfCombinator & DispatchPropsOfControl;

/**
* Props of an array control.
*/
Expand Down
7 changes: 6 additions & 1 deletion packages/examples/src/arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,18 @@ export const schema = {
}
}
}
}
},
foo:{type:'string'}
}
};

export const uischema = {
type: 'VerticalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/foo'
},
{
type: 'Control',
scope: '#/properties/comments'
Expand Down
1 change: 1 addition & 0 deletions packages/material/src/cells/MaterialTimeCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ export const MaterialTimeCell = (props: CellProps & WithClassname) => (
<MuiInputTime {...props} />
);
export const materialTimeCellTester: RankedTester = rankWith(2, isTimeControl);

export default withJsonFormsCellProps(MaterialTimeCell);
3 changes: 2 additions & 1 deletion packages/material/src/complex/MaterialAllOfRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
} from '@jsonforms/core';
import { JsonFormsDispatch, withJsonFormsAllOfProps } from '@jsonforms/react';

const MaterialAllOfRenderer = ({
export const MaterialAllOfRenderer = ({
schema,
rootSchema,
visible,
Expand Down Expand Up @@ -95,4 +95,5 @@ export const materialAllOfControlTester: RankedTester = rankWith(
3,
isAllOfControl
);

export default withJsonFormsAllOfProps(MaterialAllOfRenderer);
3 changes: 2 additions & 1 deletion packages/material/src/complex/MaterialAnyOfRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { JsonFormsDispatch, withJsonFormsAnyOfProps } from '@jsonforms/react';
import { Hidden, Tab, Tabs } from '@material-ui/core';
import CombinatorProperties from './CombinatorProperties';

const MaterialAnyOfRenderer = ({
export const MaterialAnyOfRenderer = ({
schema,
rootSchema,
indexOfFittingSchema,
Expand Down Expand Up @@ -97,4 +97,5 @@ export const materialAnyOfControlTester: RankedTester = rankWith(
3,
isAnyOfControl
);

export default withJsonFormsAnyOfProps(MaterialAnyOfRenderer);
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const MaterialEnumArrayRenderer = ({
data,
addItem,
removeItem,
handleChange,
...otherProps
}: ControlProps & OwnPropsOfEnum & DispatchPropsOfMultiEnumControl) => {
return (
Expand Down
3 changes: 2 additions & 1 deletion packages/material/src/complex/MaterialObjectRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { JsonFormsDispatch, withJsonFormsDetailProps } from '@jsonforms/react';
import { Hidden } from '@material-ui/core';
import React, { useMemo } from 'react';

const MaterialObjectRenderer = ({
export const MaterialObjectRenderer = ({
renderers,
cells,
uischemas,
Expand Down Expand Up @@ -84,4 +84,5 @@ export const materialObjectControlTester: RankedTester = rankWith(
2,
isObjectControl
);

export default withJsonFormsDetailProps(MaterialObjectRenderer);
12 changes: 6 additions & 6 deletions packages/material/src/complex/MaterialOneOfRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import React, { useCallback, useState } from 'react';
import isEmpty from 'lodash/isEmpty';

import {
CombinatorProps,
CombinatorRendererProps,
createCombinatorRenderInfos,
createDefaultValue,
isOneOfControl,
Expand Down Expand Up @@ -57,21 +57,20 @@ export interface OwnOneOfProps extends OwnPropsOfControl {
indexOfFittingSchema?: number;
}

const oneOf = 'oneOf';
const MaterialOneOfRenderer =
({ handleChange, schema, path, renderers, cells, rootSchema, id, visible, indexOfFittingSchema, uischema, uischemas, data }: CombinatorProps) => {
export const MaterialOneOfRenderer =
({ handleChange, schema, path, renderers, cells, rootSchema, id, visible, indexOfFittingSchema, uischema, uischemas, data }: CombinatorRendererProps) => {
const [open, setOpen] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(indexOfFittingSchema || 0);
const [newSelectedIndex, setNewSelectedIndex] = useState(0);
const handleClose = useCallback(() => setOpen(false), [setOpen]);
const cancel = useCallback(() => {
setOpen(false);
}, [setOpen]);
const _schema = resolveSubSchemas(schema, rootSchema, oneOf);
const _schema = resolveSubSchemas(schema, rootSchema, 'oneOf');
const oneOfRenderInfos = createCombinatorRenderInfos(
(_schema as JsonSchema).oneOf,
rootSchema,
oneOf,
'oneOf',
uischema,
path,
uischemas
Expand Down Expand Up @@ -150,4 +149,5 @@ const MaterialOneOfRenderer =
};

export const materialOneOfControlTester: RankedTester = rankWith(3, isOneOfControl);

export default withJsonFormsOneOfProps(MaterialOneOfRenderer);
60 changes: 36 additions & 24 deletions packages/material/src/complex/MaterialTableControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
} from '@jsonforms/react';
import startCase from 'lodash/startCase';
import range from 'lodash/range';
import React, { Fragment } from 'react';
import React, { Fragment, useMemo } from 'react';
import {
FormHelperText,
Grid,
Expand Down Expand Up @@ -195,20 +195,19 @@ const controlWithoutLabel = (scope: string): ControlElement => ({
label: false
});

const NonEmptyCell = (ownProps: OwnPropsOfNonEmptyCell) => {
const ctx = useJsonForms();
const {
path,
propName,
schema,
rootSchema,
errors,
enabled,
renderers,
cells
} = ctxToNonEmptyCellProps(ctx, ownProps);
interface NonEmptyCellComponentProps {
path: string,
propName?: string,
schema: JsonSchema,
rootSchema: JsonSchema,
errors: string,
enabled: boolean,
renderers?: JsonFormsRendererRegistryEntry[],
cells?: JsonFormsCellRendererRegistryEntry[],
isValid: boolean
}
const NonEmptyCellComponent = React.memo(({path, propName, schema,rootSchema, errors, enabled, renderers, cells, isValid}:NonEmptyCellComponentProps) => {

const isValid = isEmpty(errors);
return (
<NoBorderTableCell>
{schema.properties ? (
Expand Down Expand Up @@ -237,35 +236,47 @@ const NonEmptyCell = (ownProps: OwnPropsOfNonEmptyCell) => {
<FormHelperText error={!isValid}>{!isValid && errors}</FormHelperText>
</NoBorderTableCell>
);
});

const NonEmptyCell = (ownProps: OwnPropsOfNonEmptyCell) => {
const ctx = useJsonForms();
const emptyCellProps = ctxToNonEmptyCellProps(ctx, ownProps);

const isValid = isEmpty(emptyCellProps.errors);
return <NonEmptyCellComponent {...emptyCellProps} isValid={isValid}/>
};

interface NonEmptyRowProps {
childPath: string;
schema: JsonSchema;
rowIndex: number;
moveUp: () => void;
moveDown: () => void;
moveUpCreator: (path:string, position: number)=> ()=> void;
moveDownCreator: (path:string, position: number)=> ()=> void;
enableUp: boolean;
enableDown: boolean;
showSortButtons: boolean;
enabled: boolean;
cells?: JsonFormsCellRendererRegistryEntry[];
path: string;
}

const NonEmptyRow = React.memo(
const NonEmptyRowComponent =
({
childPath,
schema,
rowIndex,
openDeleteDialog,
moveUp,
moveDown,
moveUpCreator,
moveDownCreator,
enableUp,
enableDown,
showSortButtons,
enabled,
cells
cells,
path
}: NonEmptyRowProps & WithDeleteDialogSupport) => {
const moveUp = useMemo(() => moveUpCreator(path, rowIndex),[moveUpCreator, path, rowIndex]);
const moveDown = useMemo(() => moveDownCreator(path, rowIndex),[moveDownCreator, path, rowIndex]);
return (
<TableRow key={childPath} hover>
{generateCells(NonEmptyCell, schema, childPath, enabled, cells)}
Expand Down Expand Up @@ -314,8 +325,8 @@ const NonEmptyRow = React.memo(
) : null}
</TableRow>
);
}
);
};
export const NonEmptyRow = React.memo(NonEmptyRowComponent);
interface TableRowsProp {
data: number;
path: string;
Expand Down Expand Up @@ -359,13 +370,14 @@ const TableRows = ({
rowIndex={index}
schema={schema}
openDeleteDialog={openDeleteDialog}
moveUp={moveUp(path, index)}
moveDown={moveDown(path, index)}
moveUpCreator={moveUp}
moveDownCreator={moveDown}
enableUp={index !== 0}
enableDown={index !== data - 1}
showSortButtons={appliedUiSchemaOptions.showSortButtons}
enabled={enabled}
cells={cells}
path={path}
/>
);
})}
Expand Down
Loading

0 comments on commit 8eeae86

Please sign in to comment.