Skip to content

Commit 8cdd5ef

Browse files
LukasBollsdirix
andauthored
fix(translations): memoize array translation (#2358)
This commit addresses an issue where array translations were created as new objects within the core module on each render cycle, causing unnecessary rerenders. By memoizing the translation object in the material renderer set, this commit prevents redundant rerenders. This commit also adds memorization to the attributes of the material oneOfRenderer to further avoid unnecessary rerendering. Co-authored-by: Stefan Dirix <sdirix@eclipsesource.com>
1 parent 52b843a commit 8cdd5ef

32 files changed

+785
-94
lines changed

packages/angular-material/src/library/layouts/array-layout.renderer.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,13 @@ import {
3333
JsonFormsAbstractControl,
3434
} from '@jsonforms/angular';
3535
import {
36+
arrayDefaultTranslations,
3637
ArrayLayoutProps,
3738
ArrayTranslations,
3839
createDefaultValue,
40+
defaultJsonFormsI18nState,
3941
findUISchema,
42+
getArrayTranslations,
4043
isObjectArrayWithNesting,
4144
JsonFormsState,
4245
mapDispatchToArrayControlProps,
@@ -169,7 +172,7 @@ export class ArrayLayoutRenderer
169172
implements OnInit, OnDestroy
170173
{
171174
noData: boolean;
172-
translations: ArrayTranslations;
175+
translations: ArrayTranslations = {};
173176
addItem: (path: string, value: any) => () => void;
174177
moveItemUp: (path: string, index: number) => () => void;
175178
moveItemDown: (path: string, index: number) => () => void;
@@ -181,9 +184,19 @@ export class ArrayLayoutRenderer
181184
constructor(jsonFormsService: JsonFormsAngularService) {
182185
super(jsonFormsService);
183186
}
184-
mapToProps(state: JsonFormsState): StatePropsOfArrayLayout {
187+
mapToProps(
188+
state: JsonFormsState
189+
): StatePropsOfArrayLayout & { translations: ArrayTranslations } {
185190
const props = mapStateToArrayLayoutProps(state, this.getOwnProps());
186-
return { ...props };
191+
const t =
192+
state.jsonforms.i18n?.translate ?? defaultJsonFormsI18nState.translate;
193+
const translations = getArrayTranslations(
194+
t,
195+
arrayDefaultTranslations,
196+
props.i18nKeyPrefix,
197+
props.label
198+
);
199+
return { ...props, translations };
187200
}
188201
remove(index: number): void {
189202
this.removeItems(this.propsPath, [index])();
@@ -211,10 +224,12 @@ export class ArrayLayoutRenderer
211224
this.moveItemDown = moveDown;
212225
this.removeItems = removeItems;
213226
}
214-
mapAdditionalProps(props: ArrayLayoutProps) {
215-
this.translations = props.translations;
227+
mapAdditionalProps(
228+
props: ArrayLayoutProps & { translations: ArrayTranslations }
229+
) {
216230
this.noData = !props.data || props.data === 0;
217231
this.uischemas = props.uischemas;
232+
this.translations = props.translations;
218233
}
219234
getProps(index: number): OwnPropsOfRenderer {
220235
const uischema = findUISchema(

packages/angular-material/src/library/other/master-detail/master.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,14 @@ import {
3636
} from '@jsonforms/angular';
3737
import {
3838
ArrayControlProps,
39+
arrayDefaultTranslations,
3940
ArrayTranslations,
4041
ControlElement,
4142
createDefaultValue,
4243
decode,
44+
defaultJsonFormsI18nState,
4345
findUISchema,
46+
getArrayTranslations,
4447
getFirstPrimitiveProp,
4548
JsonFormsState,
4649
mapDispatchToArrayControlProps,
@@ -194,7 +197,9 @@ export class MasterListComponent
194197
this.removeItems = removeItems;
195198
}
196199

197-
mapAdditionalProps(props: ArrayControlProps) {
200+
mapAdditionalProps(
201+
props: ArrayControlProps & { translations: ArrayTranslations }
202+
) {
198203
const { data, path, schema, uischema } = props;
199204
const controlElement = uischema as ControlElement;
200205
this.propsPath = props.path;
@@ -282,9 +287,19 @@ export class MasterListComponent
282287
this.removeItems(this.propsPath, [item])();
283288
}
284289

285-
protected mapToProps(state: JsonFormsState): StatePropsOfArrayControl {
290+
protected mapToProps(
291+
state: JsonFormsState
292+
): StatePropsOfArrayControl & { translations: ArrayTranslations } {
286293
const props = mapStateToArrayControlProps(state, this.getOwnProps());
287-
return { ...props };
294+
const t =
295+
state.jsonforms.i18n?.translate ?? defaultJsonFormsI18nState.translate;
296+
const translations = getArrayTranslations(
297+
t,
298+
arrayDefaultTranslations,
299+
props.i18nKeyPrefix,
300+
props.label
301+
);
302+
return { ...props, translations };
288303
}
289304
}
290305

packages/angular-material/src/library/other/table.renderer.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,15 +158,17 @@ export class TableRenderer extends JsonFormsArrayControl implements OnInit {
158158
moveItemUp: (path: string, index: number) => () => void;
159159
moveItemDown: (path: string, index: number) => () => void;
160160
removeItems: (path: string, toDelete: number[]) => () => void;
161-
translations: ArrayTranslations;
161+
translations: ArrayTranslations = {};
162162

163163
constructor(jsonformsService: JsonFormsAngularService) {
164164
super(jsonformsService);
165165
}
166166
trackElement(index: number, _element: any) {
167167
return index ? index : null;
168168
}
169-
mapAdditionalProps(props: ArrayControlProps) {
169+
mapAdditionalProps(
170+
props: ArrayControlProps & { translations: ArrayTranslations }
171+
) {
170172
this.items = this.generateCells(props.schema, props.path);
171173
this.displayedColumns = this.items.map((item) => item.property);
172174
if (this.isEnabled()) {

packages/angular/src/library/array-control.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
THE SOFTWARE.
2424
*/
2525
import {
26+
arrayDefaultTranslations,
27+
ArrayTranslations,
28+
defaultJsonFormsI18nState,
29+
getArrayTranslations,
2630
JsonFormsState,
2731
mapStateToArrayControlProps,
2832
StatePropsOfArrayControl,
@@ -34,8 +38,18 @@ export class JsonFormsArrayControl
3438
extends JsonFormsAbstractControl<StatePropsOfArrayControl>
3539
implements OnInit, OnDestroy
3640
{
37-
protected mapToProps(state: JsonFormsState): StatePropsOfArrayControl {
41+
protected mapToProps(
42+
state: JsonFormsState
43+
): StatePropsOfArrayControl & { translations: ArrayTranslations } {
3844
const props = mapStateToArrayControlProps(state, this.getOwnProps());
39-
return { ...props };
45+
const t =
46+
state.jsonforms.i18n?.translate ?? defaultJsonFormsI18nState.translate;
47+
const translations = getArrayTranslations(
48+
t,
49+
arrayDefaultTranslations,
50+
props.i18nKeyPrefix,
51+
props.label
52+
);
53+
return { ...props, translations };
4054
}
4155
}

packages/core/src/mappers/renderer.ts

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,8 @@ import {
4747
getI18nKey,
4848
getI18nKeyPrefix,
4949
getI18nKeyPrefixBySchema,
50-
getArrayTranslations,
51-
CombinatorTranslations,
52-
getCombinatorTranslations,
53-
combinatorDefaultTranslations,
5450
getTranslator,
5551
getErrorTranslator,
56-
arrayDefaultTranslations,
5752
ArrayTranslations,
5853
} from '../i18n';
5954
import cloneDeep from 'lodash/cloneDeep';
@@ -790,7 +785,6 @@ export interface ControlWithDetailProps
790785
*/
791786
export interface StatePropsOfArrayControl
792787
extends StatePropsOfControlWithDetail {
793-
translations: ArrayTranslations;
794788
childErrors?: ErrorObject[];
795789
}
796790

@@ -805,12 +799,11 @@ export const mapStateToArrayControlProps = (
805799
state: JsonFormsState,
806800
ownProps: OwnPropsOfControl
807801
): StatePropsOfArrayControl => {
808-
const { path, schema, uischema, i18nKeyPrefix, label, ...props } =
802+
const { path, schema, uischema, label, ...props } =
809803
mapStateToControlWithDetailProps(state, ownProps);
810804

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

815808
return {
816809
...props,
@@ -821,12 +814,6 @@ export const mapStateToArrayControlProps = (
821814
childErrors,
822815
renderers: ownProps.renderers || getRenderers(state),
823816
cells: ownProps.cells || getCells(state),
824-
translations: getArrayTranslations(
825-
t,
826-
arrayDefaultTranslations,
827-
i18nKeyPrefix,
828-
label
829-
),
830817
};
831818
};
832819

@@ -1061,7 +1048,6 @@ export interface StatePropsOfCombinator extends StatePropsOfControl {
10611048
indexOfFittingSchema: number;
10621049
uischemas: JsonFormsUISchemaRegistryEntry[];
10631050
data: any;
1064-
translations: CombinatorTranslations;
10651051
}
10661052

10671053
export const mapStateToCombinatorRendererProps = (
@@ -1073,13 +1059,6 @@ export const mapStateToCombinatorRendererProps = (
10731059
mapStateToControlProps(state, ownProps);
10741060

10751061
const ajv = state.jsonforms.core.ajv;
1076-
const t = getTranslator()(state);
1077-
const translations = getCombinatorTranslations(
1078-
t,
1079-
combinatorDefaultTranslations,
1080-
i18nKeyPrefix,
1081-
label
1082-
);
10831062
const structuralKeywords = [
10841063
'required',
10851064
'additionalProperties',
@@ -1126,7 +1105,6 @@ export const mapStateToCombinatorRendererProps = (
11261105
label,
11271106
indexOfFittingSchema,
11281107
uischemas: getUISchemas(state),
1129-
translations,
11301108
};
11311109
};
11321110

@@ -1161,7 +1139,6 @@ export const mapStateToOneOfProps = (
11611139

11621140
export interface StatePropsOfArrayLayout extends StatePropsOfControlWithDetail {
11631141
data: number;
1164-
translations: ArrayTranslations;
11651142
minItems?: number;
11661143
disableRemove?: boolean;
11671144
disableAdd?: boolean;
@@ -1177,7 +1154,7 @@ export const mapStateToArrayLayoutProps = (
11771154
state: JsonFormsState,
11781155
ownProps: OwnPropsOfControl
11791156
): StatePropsOfArrayLayout => {
1180-
const { path, schema, uischema, errors, i18nKeyPrefix, label, ...props } =
1157+
const { path, schema, uischema, errors, label, ...props } =
11811158
mapStateToControlWithDetailProps(state, ownProps);
11821159

11831160
const resolvedSchema = Resolve.schema(schema, 'items', props.rootSchema);
@@ -1205,12 +1182,6 @@ export const mapStateToArrayLayoutProps = (
12051182
data: props.data ? props.data.length : 0,
12061183
errors: allErrors,
12071184
minItems: schema.minItems,
1208-
translations: getArrayTranslations(
1209-
t,
1210-
arrayDefaultTranslations,
1211-
i18nKeyPrefix,
1212-
label
1213-
),
12141185
};
12151186
};
12161187

packages/material-renderers/src/additional/MaterialListWithDetailRenderer.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import {
2626
and,
2727
ArrayLayoutProps,
28+
ArrayTranslations,
2829
composePaths,
2930
computeLabel,
3031
createDefaultValue,
@@ -36,7 +37,9 @@ import {
3637
} from '@jsonforms/core';
3738
import {
3839
JsonFormsDispatch,
40+
withArrayTranslationProps,
3941
withJsonFormsArrayLayoutProps,
42+
withTranslateProps,
4043
} from '@jsonforms/react';
4144
import { Grid, List, Typography } from '@mui/material';
4245
import map from 'lodash/map';
@@ -63,11 +66,11 @@ export const MaterialListWithDetailRenderer = ({
6366
cells,
6467
config,
6568
rootSchema,
66-
translations,
6769
description,
6870
disableAdd,
6971
disableRemove,
70-
}: ArrayLayoutProps) => {
72+
translations,
73+
}: ArrayLayoutProps & { translations: ArrayTranslations }) => {
7174
const [selectedIndex, setSelectedIndex] = useState(undefined);
7275
const handleRemoveItem = useCallback(
7376
(p: string, value: any) => () => {
@@ -101,6 +104,7 @@ export const MaterialListWithDetailRenderer = ({
101104
),
102105
[uischemas, schema, uischema.scope, path, uischema, rootSchema]
103106
);
107+
104108
const appliedUiSchemaOptions = merge({}, config, uischema.options);
105109
const doDisableAdd = disableAdd || appliedUiSchemaOptions.disableAdd;
106110
const doDisableRemove = disableRemove || appliedUiSchemaOptions.disableRemove;
@@ -179,4 +183,6 @@ export const materialListWithDetailTester: RankedTester = rankWith(
179183
and(uiTypeIs('ListWithDetail'), isObjectArray)
180184
);
181185

182-
export default withJsonFormsArrayLayoutProps(MaterialListWithDetailRenderer);
186+
export default withJsonFormsArrayLayoutProps(
187+
withTranslateProps(withArrayTranslationProps(MaterialListWithDetailRenderer))
188+
);

packages/material-renderers/src/complex/MaterialArrayControlRenderer.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,28 @@
2525
import React, { useCallback, useState } from 'react';
2626
import {
2727
ArrayLayoutProps,
28+
ArrayTranslations,
2829
RankedTester,
2930
isObjectArrayControl,
3031
isPrimitiveArrayControl,
3132
or,
3233
rankWith,
3334
} from '@jsonforms/core';
34-
import { withJsonFormsArrayLayoutProps } from '@jsonforms/react';
35+
import {
36+
withArrayTranslationProps,
37+
withJsonFormsArrayLayoutProps,
38+
withTranslateProps,
39+
} from '@jsonforms/react';
3540
import { MaterialTableControl } from './MaterialTableControl';
3641
import { DeleteDialog } from './DeleteDialog';
3742

38-
export const MaterialArrayControlRenderer = (props: ArrayLayoutProps) => {
43+
export const MaterialArrayControlRenderer = (
44+
props: ArrayLayoutProps & { translations: ArrayTranslations }
45+
) => {
3946
const [open, setOpen] = useState(false);
4047
const [path, setPath] = useState(undefined);
4148
const [rowData, setRowData] = useState(undefined);
42-
const { removeItems, visible } = props;
49+
const { removeItems, visible, translations } = props;
4350

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

6471
return (
6572
<>
66-
<MaterialTableControl {...props} openDeleteDialog={openDeleteDialog} />
73+
<MaterialTableControl
74+
{...props}
75+
openDeleteDialog={openDeleteDialog}
76+
translations={translations}
77+
/>
6778
<DeleteDialog
6879
open={open}
6980
onCancel={deleteCancel}
7081
onConfirm={deleteConfirm}
7182
onClose={deleteClose}
72-
acceptText={props.translations.deleteDialogAccept}
73-
declineText={props.translations.deleteDialogDecline}
74-
title={props.translations.deleteDialogTitle}
75-
message={props.translations.deleteDialogMessage}
83+
acceptText={translations.deleteDialogAccept}
84+
declineText={translations.deleteDialogDecline}
85+
title={translations.deleteDialogTitle}
86+
message={translations.deleteDialogMessage}
7687
/>
7788
</>
7889
);
@@ -83,4 +94,6 @@ export const materialArrayControlTester: RankedTester = rankWith(
8394
or(isObjectArrayControl, isPrimitiveArrayControl)
8495
);
8596

86-
export default withJsonFormsArrayLayoutProps(MaterialArrayControlRenderer);
97+
export default withJsonFormsArrayLayoutProps(
98+
withTranslateProps(withArrayTranslationProps(MaterialArrayControlRenderer))
99+
);

0 commit comments

Comments
 (0)