Skip to content

Commit

Permalink
fix(translations): memoize array translation
Browse files Browse the repository at this point in the history
This commit addresses an issue where array translations were created as new objects within the core module for each render
cycle, causing unnecessary rerenders. By memoizing the translation object in the material renderer set, this commit optimizes
performance and prevents redundant rerenders.

closes eclipsesource#2342
  • Loading branch information
LukasBoll committed Aug 15, 2024
1 parent 26fd0e9 commit 0e697e7
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ import {
JsonFormsAbstractControl,
} from '@jsonforms/angular';
import {
arrayDefaultTranslations,
ArrayLayoutProps,
ArrayTranslations,
createDefaultValue,
defaultJsonFormsI18nState,
findUISchema,
getArrayTranslations,
isObjectArrayWithNesting,
JsonFormsState,
mapDispatchToArrayControlProps,
Expand Down Expand Up @@ -169,7 +172,7 @@ export class ArrayLayoutRenderer
implements OnInit, OnDestroy
{
noData: boolean;
translations: ArrayTranslations;
translations: ArrayTranslations = {};
addItem: (path: string, value: any) => () => void;
moveItemUp: (path: string, index: number) => () => void;
moveItemDown: (path: string, index: number) => () => void;
Expand All @@ -181,9 +184,19 @@ export class ArrayLayoutRenderer
constructor(jsonFormsService: JsonFormsAngularService) {
super(jsonFormsService);
}
mapToProps(state: JsonFormsState): StatePropsOfArrayLayout {
mapToProps(
state: JsonFormsState
): StatePropsOfArrayLayout & { translations: ArrayTranslations } {
const props = mapStateToArrayLayoutProps(state, this.getOwnProps());
return { ...props };
const t =
state.jsonforms.i18n?.translate ?? defaultJsonFormsI18nState.translate;
const translations = getArrayTranslations(
t,
arrayDefaultTranslations,
props.i18nKeyPrefix,
props.label
);
return { ...props, translations };
}
remove(index: number): void {
this.removeItems(this.propsPath, [index])();
Expand Down Expand Up @@ -211,10 +224,12 @@ export class ArrayLayoutRenderer
this.moveItemDown = moveDown;
this.removeItems = removeItems;
}
mapAdditionalProps(props: ArrayLayoutProps) {
this.translations = props.translations;
mapAdditionalProps(
props: ArrayLayoutProps & { translations: ArrayTranslations }
) {
this.noData = !props.data || props.data === 0;
this.uischemas = props.uischemas;
this.translations = props.translations;
}
getProps(index: number): OwnPropsOfRenderer {
const uischema = findUISchema(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@ import {
} from '@jsonforms/angular';
import {
ArrayControlProps,
arrayDefaultTranslations,
ArrayTranslations,
ControlElement,
createDefaultValue,
decode,
defaultJsonFormsI18nState,
findUISchema,
getArrayTranslations,
getFirstPrimitiveProp,
JsonFormsState,
mapDispatchToArrayControlProps,
Expand Down Expand Up @@ -194,7 +197,9 @@ export class MasterListComponent
this.removeItems = removeItems;
}

mapAdditionalProps(props: ArrayControlProps) {
mapAdditionalProps(
props: ArrayControlProps & { translations: ArrayTranslations }
) {
const { data, path, schema, uischema } = props;
const controlElement = uischema as ControlElement;
this.propsPath = props.path;
Expand Down Expand Up @@ -282,9 +287,19 @@ export class MasterListComponent
this.removeItems(this.propsPath, [item])();
}

protected mapToProps(state: JsonFormsState): StatePropsOfArrayControl {
protected mapToProps(
state: JsonFormsState
): StatePropsOfArrayControl & { translations: ArrayTranslations } {
const props = mapStateToArrayControlProps(state, this.getOwnProps());
return { ...props };
const t =
state.jsonforms.i18n?.translate ?? defaultJsonFormsI18nState.translate;
const translations = getArrayTranslations(
t,
arrayDefaultTranslations,
props.i18nKeyPrefix,
props.label
);
return { ...props, translations };
}
}

Expand Down
6 changes: 4 additions & 2 deletions packages/angular-material/src/library/other/table.renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,17 @@ export class TableRenderer extends JsonFormsArrayControl implements OnInit {
moveItemUp: (path: string, index: number) => () => void;
moveItemDown: (path: string, index: number) => () => void;
removeItems: (path: string, toDelete: number[]) => () => void;
translations: ArrayTranslations;
translations: ArrayTranslations = {};

constructor(jsonformsService: JsonFormsAngularService) {
super(jsonformsService);
}
trackElement(index: number, _element: any) {
return index ? index : null;
}
mapAdditionalProps(props: ArrayControlProps) {
mapAdditionalProps(
props: ArrayControlProps & { translations: ArrayTranslations }
) {
this.items = this.generateCells(props.schema, props.path);
this.displayedColumns = this.items.map((item) => item.property);
if (this.isEnabled()) {
Expand Down
18 changes: 16 additions & 2 deletions packages/angular/src/library/array-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
THE SOFTWARE.
*/
import {
arrayDefaultTranslations,
ArrayTranslations,
defaultJsonFormsI18nState,
getArrayTranslations,
JsonFormsState,
mapStateToArrayControlProps,
StatePropsOfArrayControl,
Expand All @@ -34,8 +38,18 @@ export class JsonFormsArrayControl
extends JsonFormsAbstractControl<StatePropsOfArrayControl>
implements OnInit, OnDestroy
{
protected mapToProps(state: JsonFormsState): StatePropsOfArrayControl {
protected mapToProps(
state: JsonFormsState
): StatePropsOfArrayControl & { translations: ArrayTranslations } {
const props = mapStateToArrayControlProps(state, this.getOwnProps());
return { ...props };
const t =
state.jsonforms.i18n?.translate ?? defaultJsonFormsI18nState.translate;
const translations = getArrayTranslations(
t,
arrayDefaultTranslations,
props.i18nKeyPrefix,
props.label
);
return { ...props, translations };
}
}
33 changes: 2 additions & 31 deletions packages/core/src/mappers/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,8 @@ import {
getI18nKey,
getI18nKeyPrefix,
getI18nKeyPrefixBySchema,
getArrayTranslations,
CombinatorTranslations,
getCombinatorTranslations,
combinatorDefaultTranslations,
getTranslator,
getErrorTranslator,
arrayDefaultTranslations,
ArrayTranslations,
} from '../i18n';
import cloneDeep from 'lodash/cloneDeep';
Expand Down Expand Up @@ -789,7 +784,6 @@ export interface ControlWithDetailProps
*/
export interface StatePropsOfArrayControl
extends StatePropsOfControlWithDetail {
translations: ArrayTranslations;
childErrors?: ErrorObject[];
}

Expand All @@ -804,12 +798,11 @@ export const mapStateToArrayControlProps = (
state: JsonFormsState,
ownProps: OwnPropsOfControl
): StatePropsOfArrayControl => {
const { path, schema, uischema, i18nKeyPrefix, label, ...props } =
const { path, schema, uischema, label, ...props } =
mapStateToControlWithDetailProps(state, ownProps);

const resolvedSchema = Resolve.schema(schema, 'items', props.rootSchema);
const childErrors = getSubErrorsAt(path, resolvedSchema)(state);
const t = getTranslator()(state);

return {
...props,
Expand All @@ -820,12 +813,6 @@ export const mapStateToArrayControlProps = (
childErrors,
renderers: ownProps.renderers || getRenderers(state),
cells: ownProps.cells || getCells(state),
translations: getArrayTranslations(
t,
arrayDefaultTranslations,
i18nKeyPrefix,
label
),
};
};

Expand Down Expand Up @@ -1060,7 +1047,6 @@ export interface StatePropsOfCombinator extends StatePropsOfControl {
indexOfFittingSchema: number;
uischemas: JsonFormsUISchemaRegistryEntry[];
data: any;
translations: CombinatorTranslations;
}

export const mapStateToCombinatorRendererProps = (
Expand All @@ -1072,13 +1058,6 @@ export const mapStateToCombinatorRendererProps = (
mapStateToControlProps(state, ownProps);

const ajv = state.jsonforms.core.ajv;
const t = getTranslator()(state);
const translations = getCombinatorTranslations(
t,
combinatorDefaultTranslations,
i18nKeyPrefix,
label
);
const structuralKeywords = [
'required',
'additionalProperties',
Expand Down Expand Up @@ -1125,7 +1104,6 @@ export const mapStateToCombinatorRendererProps = (
label,
indexOfFittingSchema,
uischemas: getUISchemas(state),
translations,
};
};

Expand Down Expand Up @@ -1160,7 +1138,6 @@ export const mapStateToOneOfProps = (

export interface StatePropsOfArrayLayout extends StatePropsOfControlWithDetail {
data: number;
translations: ArrayTranslations;
minItems?: number;
disableRemove?: boolean;
disableAdd?: boolean;
Expand All @@ -1176,7 +1153,7 @@ export const mapStateToArrayLayoutProps = (
state: JsonFormsState,
ownProps: OwnPropsOfControl
): StatePropsOfArrayLayout => {
const { path, schema, uischema, errors, i18nKeyPrefix, label, ...props } =
const { path, schema, uischema, errors, label, ...props } =
mapStateToControlWithDetailProps(state, ownProps);

const resolvedSchema = Resolve.schema(schema, 'items', props.rootSchema);
Expand Down Expand Up @@ -1204,12 +1181,6 @@ export const mapStateToArrayLayoutProps = (
data: props.data ? props.data.length : 0,
errors: allErrors,
minItems: schema.minItems,
translations: getArrayTranslations(
t,
arrayDefaultTranslations,
i18nKeyPrefix,
label
),
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import {
and,
ArrayLayoutProps,
ArrayTranslations,
composePaths,
computeLabel,
createDefaultValue,
Expand All @@ -36,7 +37,9 @@ import {
} from '@jsonforms/core';
import {
JsonFormsDispatch,
withArrayTranslationProps,
withJsonFormsArrayLayoutProps,
withTranslateProps,
} from '@jsonforms/react';
import { Grid, List, Typography } from '@mui/material';
import map from 'lodash/map';
Expand All @@ -63,11 +66,11 @@ export const MaterialListWithDetailRenderer = ({
cells,
config,
rootSchema,
translations,
description,
disableAdd,
disableRemove,
}: ArrayLayoutProps) => {
translations,
}: ArrayLayoutProps & { translations: ArrayTranslations }) => {
const [selectedIndex, setSelectedIndex] = useState(undefined);
const handleRemoveItem = useCallback(
(p: string, value: any) => () => {
Expand Down Expand Up @@ -101,6 +104,7 @@ export const MaterialListWithDetailRenderer = ({
),
[uischemas, schema, uischema.scope, path, uischema, rootSchema]
);

const appliedUiSchemaOptions = merge({}, config, uischema.options);
const doDisableAdd = disableAdd || appliedUiSchemaOptions.disableAdd;
const doDisableRemove = disableRemove || appliedUiSchemaOptions.disableRemove;
Expand Down Expand Up @@ -179,4 +183,6 @@ export const materialListWithDetailTester: RankedTester = rankWith(
and(uiTypeIs('ListWithDetail'), isObjectArray)
);

export default withJsonFormsArrayLayoutProps(MaterialListWithDetailRenderer);
export default withJsonFormsArrayLayoutProps(
withTranslateProps(withArrayTranslationProps(MaterialListWithDetailRenderer))
);
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,28 @@
import React, { useCallback, useState } from 'react';
import {
ArrayLayoutProps,
ArrayTranslations,
RankedTester,
isObjectArrayControl,
isPrimitiveArrayControl,
or,
rankWith,
} from '@jsonforms/core';
import { withJsonFormsArrayLayoutProps } from '@jsonforms/react';
import {
withArrayTranslationProps,
withJsonFormsArrayLayoutProps,
withTranslateProps,
} from '@jsonforms/react';
import { MaterialTableControl } from './MaterialTableControl';
import { DeleteDialog } from './DeleteDialog';

export const MaterialArrayControlRenderer = (props: ArrayLayoutProps) => {
export const MaterialArrayControlRenderer = (
props: ArrayLayoutProps & { translations: ArrayTranslations }
) => {
const [open, setOpen] = useState(false);
const [path, setPath] = useState(undefined);
const [rowData, setRowData] = useState(undefined);
const { removeItems, visible } = props;
const { removeItems, visible, translations } = props;

const openDeleteDialog = useCallback(
(p: string, rowIndex: number) => {
Expand All @@ -63,16 +70,20 @@ export const MaterialArrayControlRenderer = (props: ArrayLayoutProps) => {

return (
<>
<MaterialTableControl {...props} openDeleteDialog={openDeleteDialog} />
<MaterialTableControl
{...props}
openDeleteDialog={openDeleteDialog}
translations={translations}
/>
<DeleteDialog
open={open}
onCancel={deleteCancel}
onConfirm={deleteConfirm}
onClose={deleteClose}
acceptText={props.translations.deleteDialogAccept}
declineText={props.translations.deleteDialogDecline}
title={props.translations.deleteDialogTitle}
message={props.translations.deleteDialogMessage}
acceptText={translations.deleteDialogAccept}
declineText={translations.deleteDialogDecline}
title={translations.deleteDialogTitle}
message={translations.deleteDialogMessage}
/>
</>
);
Expand All @@ -83,4 +94,6 @@ export const materialArrayControlTester: RankedTester = rankWith(
or(isObjectArrayControl, isPrimitiveArrayControl)
);

export default withJsonFormsArrayLayoutProps(MaterialArrayControlRenderer);
export default withJsonFormsArrayLayoutProps(
withTranslateProps(withArrayTranslationProps(MaterialArrayControlRenderer))
);
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,8 @@ const TableRows = ({
};

export class MaterialTableControl extends React.Component<
ArrayLayoutProps & WithDeleteDialogSupport,
ArrayLayoutProps &
WithDeleteDialogSupport & { translations: ArrayTranslations },
any
> {
addItem = (path: string, value: any) => this.props.addItem(path, value);
Expand Down
Loading

0 comments on commit 0e697e7

Please sign in to comment.