Skip to content

Commit 81e4dd6

Browse files
committed
Improve React Vanilla bindings
The React Vanilla renderers add additional bindings for style customizations. The calculated props are now properly memoized so that the standard React memoization can take effect.
1 parent 4fa6f58 commit 81e4dd6

File tree

7 files changed

+59
-34
lines changed

7 files changed

+59
-34
lines changed

packages/vanilla/src/layouts/GroupLayout.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,13 @@ import { withVanillaControlProps } from '../util';
3737
*/
3838
export const groupTester: RankedTester = rankWith(1, uiTypeIs('Group'));
3939

40-
export const GroupLayoutRenderer: FunctionComponent<RendererProps & VanillaRendererProps> = (
40+
export const GroupLayoutRenderer = (props: RendererProps & VanillaRendererProps) => {
41+
const {data, ...otherProps} = props;
42+
// We don't hand over data to the layout renderer to avoid rerendering it with every data change
43+
return <GroupLayoutRendererComponent {...otherProps}/>;
44+
}
45+
46+
const GroupLayoutRendererComponent: FunctionComponent<RendererProps & VanillaRendererProps> = React.memo((
4147
{
4248
schema,
4349
uischema,
@@ -67,6 +73,6 @@ export const GroupLayoutRenderer: FunctionComponent<RendererProps & VanillaRende
6773
{renderChildren(group, schema, childClassNames, path)}
6874
</fieldset>
6975
);
70-
};
76+
});
7177

7278
export default withVanillaControlProps(withJsonFormsLayoutProps(GroupLayoutRenderer));

packages/vanilla/src/layouts/HorizontalLayout.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,13 @@ import { VanillaRendererProps } from '../index';
4242
*/
4343
export const horizontalLayoutTester: RankedTester = rankWith(1, uiTypeIs('HorizontalLayout'));
4444

45-
const HorizontalLayoutRenderer: FunctionComponent<RendererProps & VanillaRendererProps> = (
45+
export const HorizontalLayoutRenderer = (props: RendererProps & VanillaRendererProps) => {
46+
const {data, ...otherProps} = props;
47+
// We don't hand over data to the layout renderer to avoid rerendering it with every data change
48+
return <HorizontalLayoutRendererComponent {...otherProps}/>;
49+
}
50+
51+
const HorizontalLayoutRendererComponent: FunctionComponent<RendererProps & VanillaRendererProps> = React.memo((
4652
{
4753
schema,
4854
uischema,
@@ -75,6 +81,6 @@ const HorizontalLayoutRenderer: FunctionComponent<RendererProps & VanillaRendere
7581
{renderChildren(horizontalLayout, schema, childClassNames, path)}
7682
</JsonFormsLayout>
7783
);
78-
};
84+
});
7985

80-
export default withVanillaControlProps(withJsonFormsLayoutProps(HorizontalLayoutRenderer));
86+
export default withVanillaControlProps(withJsonFormsLayoutProps(HorizontalLayoutRenderer, false));

packages/vanilla/src/layouts/JsonFormsLayout.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@ import React from 'react';
2626
import { RendererProps } from '@jsonforms/core';
2727
import { VanillaRendererProps, WithChildren } from '../index';
2828

29-
// tslint:disable:variable-name
3029
export const JsonFormsLayout =
3130
({ className, children, visible }: RendererProps & VanillaRendererProps & WithChildren) => {
32-
// tslint:enable:variable-name
3331

3432
return (
3533
<div

packages/vanilla/src/layouts/VerticalLayout.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,13 @@ import { VanillaRendererProps } from '../index';
4242
*/
4343
export const verticalLayoutTester: RankedTester = rankWith(1, uiTypeIs('VerticalLayout'));
4444

45-
export const VerticalLayoutRenderer: FunctionComponent<RendererProps & VanillaRendererProps> = (
45+
export const VerticalLayoutRenderer = (props: RendererProps & VanillaRendererProps) => {
46+
const {data, ...otherProps} = props;
47+
// We don't hand over data to the layout renderer to avoid rerendering it with every data change
48+
return <VerticalLayoutRendererComponent {...otherProps}/>;
49+
}
50+
51+
const VerticalLayoutRendererComponent: FunctionComponent<RendererProps & VanillaRendererProps> = React.memo((
4652
{
4753
schema,
4854
uischema,
@@ -74,6 +80,6 @@ export const VerticalLayoutRenderer: FunctionComponent<RendererProps & VanillaRe
7480
{renderChildren(verticalLayout, schema, childClassNames, path)}
7581
</JsonFormsLayout>
7682
);
77-
};
83+
});
7884

79-
export default withVanillaControlProps(withJsonFormsLayoutProps(VerticalLayoutRenderer));
85+
export default withVanillaControlProps(withJsonFormsLayoutProps(VerticalLayoutRenderer, false));

packages/vanilla/src/util/index.tsx

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import {
3939
import { useJsonForms } from '@jsonforms/react';
4040
import { getStyle, getStyleAsClassName } from '../reducers';
4141
import { VanillaRendererProps } from '../index';
42-
import { ComponentType } from 'react';
42+
import { ComponentType, useMemo } from 'react';
4343
import { findStyle, findStyleAsClassName } from '../reducers/styling';
4444
import { useStyles } from '../styles';
4545

@@ -96,7 +96,7 @@ export const withVanillaControlProps = (Component: ComponentType<any>) => (props
9696
const controlElement = props.uischema as ControlElement;
9797
const config = ctx.config;
9898
const trim = config && config.trim;
99-
const styles = findStyle(contextStyles)('control');
99+
const styles = useMemo(() => findStyle(contextStyles)('control'), [contextStyles]);
100100
let classNames: string[] = !isEmpty(controlElement.scope)
101101
? styles.concat([`${convertToValidClassName(controlElement.scope)}`])
102102
: [''];
@@ -105,24 +105,33 @@ export const withVanillaControlProps = (Component: ComponentType<any>) => (props
105105
classNames = classNames.concat(findStyle(contextStyles)('control.trim'));
106106
}
107107
const isValid = isEmpty(props.errors);
108-
const labelClass = findStyleAsClassName(contextStyles)('control.label');
109-
const descriptionClassName = findStyleAsClassName(contextStyles)('input.description');
110-
const validationClassName = findStyleAsClassName(contextStyles)('control.validation');
111-
const validationErrorClassName = findStyleAsClassName(contextStyles)('control.validation.error');
108+
const labelClass = useMemo(() => findStyleAsClassName(contextStyles)('control.label'), [contextStyles]);
109+
const descriptionClassName = useMemo(() => findStyleAsClassName(contextStyles)('input.description'), [contextStyles]);
110+
const validationClassName = useMemo(() => findStyleAsClassName(contextStyles)('control.validation'), [contextStyles]);
111+
const validationErrorClassName = useMemo(() => findStyleAsClassName(contextStyles)('control.validation.error'), [contextStyles]);
112112
const inputClassName = ['validate'].concat(isValid ? 'valid' : 'invalid');
113+
114+
const getStyleAsClassName = useMemo(() => findStyleAsClassName(contextStyles), [contextStyles]);
115+
const getStyle = useMemo(() => findStyle(contextStyles), [contextStyles]);
116+
117+
const wrapper = classNames.join(' ');
118+
const input = inputClassName.join(' ');
119+
120+
const classNamesProp = useMemo(() => ({
121+
wrapper,
122+
input,
123+
label: labelClass,
124+
description: descriptionClassName,
125+
validation: validationClassName,
126+
validationError: validationErrorClassName
127+
}), [wrapper, input, labelClass, descriptionClassName, validationClassName, validationErrorClassName]);
128+
113129
return (
114130
<Component
115131
{...props}
116-
getStyleAsClassName={findStyleAsClassName(contextStyles)}
117-
getStyle={findStyle(contextStyles)}
118-
classNames={{
119-
wrapper: classNames.join(' '),
120-
input: inputClassName.join(' '),
121-
label: labelClass,
122-
description: descriptionClassName,
123-
validation: validationClassName,
124-
validationError: validationErrorClassName
125-
}}
132+
getStyleAsClassName={getStyleAsClassName}
133+
getStyle={getStyle}
134+
classNames={classNamesProp}
126135
/>
127136
);
128137
}

packages/vanilla/test/renderers/HorizontalLayout.test.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ describe('Horizontal layout', () => {
6868
</JsonFormsStateProvider>
6969
);
7070

71-
const horizontalLayout = wrapper.find(HorizontalLayoutRenderer).getDOMNode() as HTMLDivElement;
71+
const horizontalLayout = wrapper.find('JsonFormsLayout').getDOMNode() as HTMLDivElement;
7272

7373
expect(horizontalLayout).toBeDefined();
7474
expect(horizontalLayout.children).toHaveLength(0);
@@ -85,7 +85,7 @@ describe('Horizontal layout', () => {
8585
<HorizontalLayoutRenderer uischema={uischema} />
8686
</JsonFormsStateProvider>
8787
);
88-
const horizontalLayout = wrapper.find(HorizontalLayoutRenderer).getDOMNode() as HTMLDivElement;
88+
const horizontalLayout = wrapper.find('JsonFormsLayout').getDOMNode();
8989
expect(horizontalLayout).toBeDefined();
9090
expect(horizontalLayout.children).toHaveLength(0);
9191
});
@@ -104,7 +104,7 @@ describe('Horizontal layout', () => {
104104
<HorizontalLayoutRenderer uischema={uischema} />
105105
</JsonFormsStateProvider>
106106
);
107-
const horizontalLayout = wrapper.find(HorizontalLayoutRenderer).getDOMNode() as HTMLDivElement;
107+
const horizontalLayout = wrapper.find('JsonFormsLayout').getDOMNode() as HTMLDivElement;
108108
expect(horizontalLayout).toBeDefined();
109109
expect(horizontalLayout.children).toHaveLength(2);
110110
});
@@ -119,7 +119,7 @@ describe('Horizontal layout', () => {
119119
/>
120120
</JsonFormsStateProvider>
121121
);
122-
const horizontalLayout = wrapper.find(HorizontalLayoutRenderer).getDOMNode() as HTMLDivElement;
122+
const horizontalLayout = wrapper.find('JsonFormsLayout').getDOMNode() as HTMLDivElement;
123123
expect(horizontalLayout.hidden).toBe(true);
124124
});
125125

@@ -130,7 +130,7 @@ describe('Horizontal layout', () => {
130130
<HorizontalLayoutRenderer uischema={fixture.uischema} />
131131
</JsonFormsStateProvider>
132132
);
133-
const horizontalLayout = wrapper.find(HorizontalLayoutRenderer).getDOMNode() as HTMLDivElement;
133+
const horizontalLayout = wrapper.find('JsonFormsLayout').getDOMNode() as HTMLDivElement;
134134
expect(horizontalLayout.hidden).toBe(false);
135135
});
136136
});

packages/vanilla/test/renderers/VerticalLayout.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe('Vertical layout', () => {
8585
<VerticalLayoutRenderer uischema={uischema} />
8686
</JsonFormsStateProvider>
8787
);
88-
const verticalLayout = wrapper.find(VerticalLayoutRenderer).getDOMNode();
88+
const verticalLayout = wrapper.find('JsonFormsLayout').getDOMNode();
8989

9090
expect(verticalLayout.tagName).toBe('DIV');
9191
expect(verticalLayout.children).toHaveLength(2);
@@ -106,7 +106,7 @@ describe('Vertical layout', () => {
106106
/>
107107
</JsonFormsStateProvider>
108108
);
109-
const verticalLayout = wrapper.find(VerticalLayoutRenderer).getDOMNode() as HTMLDivElement;
109+
const verticalLayout = wrapper.find('JsonFormsLayout').getDOMNode() as HTMLDivElement;
110110
expect(verticalLayout.hidden).toBe(true);
111111
});
112112

@@ -122,7 +122,7 @@ describe('Vertical layout', () => {
122122
<VerticalLayoutRenderer uischema={uischema} />
123123
</JsonFormsStateProvider>
124124
);
125-
const verticalLayout = wrapper.find(VerticalLayoutRenderer).getDOMNode() as HTMLDivElement;
125+
const verticalLayout = wrapper.find('JsonFormsLayout').getDOMNode() as HTMLDivElement;
126126
expect(verticalLayout.hidden).toBe(false);
127127
});
128128
});

0 commit comments

Comments
 (0)