Skip to content

Commit 33a4848

Browse files
authored
Merge pull request #1159 from lowcoder-org/minimize-rerendering-2
Added memoization to minimize re-rendering
2 parents fe9a35c + 4bafc34 commit 33a4848

File tree

14 files changed

+262
-200
lines changed

14 files changed

+262
-200
lines changed

client/packages/lowcoder/src/comps/comps/gridItemComp.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const TmpComp = withTypeAndChildren<
6262
childrenMap
6363
);
6464

65-
function CachedView(props: { comp: Comp; name: string }) {
65+
const CachedView = React.memo((props: { comp: Comp; name: string }) => {
6666
return React.useMemo(
6767
() => (
6868
<Profiler id={props.name} onRender={profilerCallback}>
@@ -73,13 +73,13 @@ function CachedView(props: { comp: Comp; name: string }) {
7373
),
7474
[props.comp, props.name]
7575
);
76-
}
76+
})
7777

78-
function CachedPropertyView(props: {
78+
const CachedPropertyView = React.memo((props: {
7979
comp: Comp;
8080
name: string;
8181
withParamsContext: WithParamsContext;
82-
}) {
82+
}) => {
8383
const prevHints = useContext(CompExposingContext);
8484
const { withParamsContext } = props;
8585
const hints = useMemo(
@@ -109,7 +109,7 @@ function CachedPropertyView(props: {
109109
</>
110110
);
111111
}, [props.comp, props.name, hints, searchText, setSearchText]);
112-
}
112+
});
113113

114114
export class GridItemComp extends TmpComp {
115115
private readonly withParamsContext: WithParamsContext = { params: {} };

client/packages/lowcoder/src/comps/comps/lazyLoadComp/lazyLoadComp.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import styled from "styled-components";
1111
import { RemoteCompInfo } from "types/remoteComp";
1212
import { withErrorBoundary } from "comps/generators/withErrorBoundary";
1313
import { ThemeContext } from "@lowcoder-ee/comps/utils/themeContext";
14+
import React from "react";
1415

1516
const ViewError = styled.div`
1617
display: flex;
@@ -50,7 +51,7 @@ interface LazyCompViewProps {
5051
errorElement?: (error: any) => React.ReactNode;
5152
}
5253

53-
function LazyCompView(props: React.PropsWithChildren<LazyCompViewProps>) {
54+
const LazyCompView = React.memo((props: React.PropsWithChildren<LazyCompViewProps>) => {
5455
const { loadComp, loadingElement, errorElement } = props;
5556
const [error, setError] = useState<any>("");
5657
const currentTheme = useContext(ThemeContext)?.theme;
@@ -83,7 +84,7 @@ function LazyCompView(props: React.PropsWithChildren<LazyCompViewProps>) {
8384
return (
8485
<WhiteLoading />
8586
);
86-
}
87+
});
8788

8889
export type LazyloadCompLoader<T = RemoteCompInfo> = () => Promise<CompConstructor | null>;
8990

client/packages/lowcoder/src/comps/comps/remoteComp/remoteComp.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { loaders } from "./loaders";
1313
import { withErrorBoundary } from "comps/generators/withErrorBoundary";
1414
import { EditorContext } from "@lowcoder-ee/comps/editorState";
1515
import { CompContext } from "@lowcoder-ee/comps/utils/compContext";
16+
import React from "react";
1617

1718
const ViewError = styled.div`
1819
display: flex;
@@ -53,7 +54,7 @@ interface RemoteCompViewProps {
5354
errorElement?: (error: any) => React.ReactNode;
5455
}
5556

56-
function RemoteCompView(props: React.PropsWithChildren<RemoteCompViewProps>) {
57+
const RemoteCompView = React.memo((props: React.PropsWithChildren<RemoteCompViewProps>) => {
5758
const { loadComp, loadingElement, errorElement, isLowcoderComp } = props;
5859
const [error, setError] = useState<any>("");
5960
const editorState = useContext(EditorContext);
@@ -93,11 +94,9 @@ function RemoteCompView(props: React.PropsWithChildren<RemoteCompViewProps>) {
9394
}
9495

9596
return (
96-
<ViewLoadingWrapper>
97-
<WhiteLoading />
98-
</ViewLoadingWrapper>
97+
<WhiteLoading />
9998
);
100-
}
99+
});
101100

102101
export function remoteComp<T extends RemoteCompInfo = RemoteCompInfo>(
103102
remoteInfo?: T,

client/packages/lowcoder/src/comps/comps/textComp.tsx

Lines changed: 86 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ import { PaddingControl } from "../controls/paddingControl";
2121
import React, { useContext, useEffect } from "react";
2222
import { EditorContext } from "comps/editorState";
2323
import { clickEvent, eventHandlerControl } from "../controls/eventHandlerControl";
24+
import { NewChildren } from "../generators/uiCompBuilder";
25+
import { RecordConstructorToComp } from "lowcoder-core";
26+
import { ToViewReturn } from "../generators/multi";
2427

2528
const EventOptions = [clickEvent] as const;
2629

@@ -130,87 +133,95 @@ const VerticalAlignmentOptions = [
130133
{ label: <AlignVerticalCenter />, value: "center" },
131134
{ label: <AlignBottom />, value: "flex-end" },
132135
] as const;
136+
const childrenMap = {
137+
text: stringExposingStateControl(
138+
"text",
139+
trans("textShow.text", { name: "{{currentUser.name}}" })
140+
),
141+
onEvent: eventHandlerControl(EventOptions),
142+
autoHeight: AutoHeightControl,
143+
type: dropdownControl(typeOptions, "markdown"),
144+
horizontalAlignment: alignWithJustifyControl(),
145+
verticalAlignment: dropdownControl(VerticalAlignmentOptions, "center"),
146+
style: styleControl(TextStyle, 'style'),
147+
animationStyle: styleControl(AnimationStyle, 'animationStyle'),
148+
margin: MarginControl,
149+
padding: PaddingControl,
150+
};
133151

134-
let TextTmpComp = (function () {
135-
const childrenMap = {
136-
text: stringExposingStateControl(
137-
"text",
138-
trans("textShow.text", { name: "{{currentUser.name}}" })
139-
),
140-
onEvent: eventHandlerControl(EventOptions),
141-
autoHeight: AutoHeightControl,
142-
type: dropdownControl(typeOptions, "markdown"),
143-
horizontalAlignment: alignWithJustifyControl(),
144-
verticalAlignment: dropdownControl(VerticalAlignmentOptions, "center"),
145-
style: styleControl(TextStyle, 'style'),
146-
animationStyle: styleControl(AnimationStyle, 'animationStyle'),
147-
margin: MarginControl,
148-
padding: PaddingControl,
149-
};
150-
return new UICompBuilder(childrenMap, (props, dispatch) => {
151-
const value = props.text.value;
152-
153-
return (
154-
<TextContainer
155-
$animationStyle={props.animationStyle}
156-
$type={props.type}
157-
$styleConfig={props.style}
158-
style={{
159-
justifyContent: props.horizontalAlignment,
160-
alignItems: props.autoHeight ? "center" : props.verticalAlignment,
161-
textAlign: props.horizontalAlignment,
162-
rotate: props.style.rotation
163-
}}
164-
onClick={() => props.onEvent("click")}
165-
>
166-
{props.type === "markdown" ? <TacoMarkDown>{value}</TacoMarkDown> : value}
167-
</TextContainer>
168-
);
169-
})
170-
.setPropertyViewFn((children) => {
171-
return (
152+
type ChildrenType = NewChildren<RecordConstructorToComp<typeof childrenMap>>;
153+
154+
const TextPropertyView = React.memo((props: {
155+
children: ChildrenType
156+
}) => {
157+
return (
158+
<>
159+
<Section name={sectionNames.basic}>
160+
{props.children.type.propertyView({
161+
label: trans("value"),
162+
tooltip: trans("textShow.valueTooltip"),
163+
radioButton: true,
164+
})}
165+
{props.children.text.propertyView({})}
166+
</Section>
167+
168+
{["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && (
169+
<Section name={sectionNames.interaction}>
170+
{hiddenPropertyView(props.children)}
171+
{props.children.onEvent.getPropertyView()}
172+
</Section>
173+
)}
174+
175+
{["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && (
172176
<>
173-
<Section name={sectionNames.basic}>
174-
{children.type.propertyView({
175-
label: trans("value"),
176-
tooltip: trans("textShow.valueTooltip"),
177+
<Section name={sectionNames.layout}>
178+
{props.children.autoHeight.getPropertyView()}
179+
{!props.children.autoHeight.getView() &&
180+
props.children.verticalAlignment.propertyView({
181+
label: trans("textShow.verticalAlignment"),
182+
radioButton: true,
183+
})}
184+
{props.children.horizontalAlignment.propertyView({
185+
label: trans("textShow.horizontalAlignment"),
177186
radioButton: true,
178187
})}
179-
{children.text.propertyView({})}
180188
</Section>
181-
182-
{["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && (
183-
<Section name={sectionNames.interaction}>
184-
{hiddenPropertyView(children)}
185-
{children.onEvent.getPropertyView()}
186-
</Section>
187-
)}
188-
189-
{["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && (
190-
<>
191-
<Section name={sectionNames.layout}>
192-
{children.autoHeight.getPropertyView()}
193-
{!children.autoHeight.getView() &&
194-
children.verticalAlignment.propertyView({
195-
label: trans("textShow.verticalAlignment"),
196-
radioButton: true,
197-
})}
198-
{children.horizontalAlignment.propertyView({
199-
label: trans("textShow.horizontalAlignment"),
200-
radioButton: true,
201-
})}
202-
</Section>
203-
<Section name={sectionNames.style}>
204-
{children.style.getPropertyView()}
205-
</Section>
206-
<Section name={sectionNames.animationStyle} hasTooltip={true}>
207-
{children.animationStyle.getPropertyView()}
208-
</Section>
209-
</>
210-
)}
189+
<Section name={sectionNames.style}>
190+
{props.children.style.getPropertyView()}
191+
</Section>
192+
<Section name={sectionNames.animationStyle} hasTooltip={true}>
193+
{props.children.animationStyle.getPropertyView()}
194+
</Section>
211195
</>
212-
);
213-
})
196+
)}
197+
</>
198+
);
199+
})
200+
201+
const TextView = React.memo((props: ToViewReturn<ChildrenType>) => {
202+
const value = props.text.value;
203+
204+
return (
205+
<TextContainer
206+
$animationStyle={props.animationStyle}
207+
$type={props.type}
208+
$styleConfig={props.style}
209+
style={{
210+
justifyContent: props.horizontalAlignment,
211+
alignItems: props.autoHeight ? "center" : props.verticalAlignment,
212+
textAlign: props.horizontalAlignment,
213+
rotate: props.style.rotation
214+
}}
215+
onClick={() => props.onEvent("click")}
216+
>
217+
{props.type === "markdown" ? <TacoMarkDown>{value}</TacoMarkDown> : value}
218+
</TextContainer>
219+
);
220+
}, (prev, next) => JSON.stringify(prev) === JSON.stringify(next));
221+
222+
let TextTmpComp = (function () {
223+
return new UICompBuilder(childrenMap, (props) => <TextView {...props} />)
224+
.setPropertyViewFn((children) => <TextPropertyView children={children} />)
214225
.build();
215226
})();
216227

client/packages/lowcoder/src/comps/controls/optionsControl.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ export function optionsControl<T extends OptionsControlType>(
359359
uniqField?: keyof ConstructorToView<T>;
360360
// manual mode list title
361361
title?: string;
362+
autoIncField?: keyof PickNumberFields<ConstructorToView<T>>;
362363
}
363364
) {
364365
type OptionViewType = ConstructorToView<T>;
@@ -369,6 +370,7 @@ export function optionsControl<T extends OptionsControlType>(
369370
manual: manualOptionsControl(VariantComp, {
370371
initOptions: config.initOptions,
371372
uniqField: config.uniqField,
373+
autoIncField: config.autoIncField,
372374
}),
373375
mapData: mapOptionsControl(VariantComp, config.uniqField),
374376
},

client/packages/lowcoder/src/comps/controls/slotControl.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { createContext, useContext } from "react";
1616
import styled from "styled-components";
1717
import { NameGenerator } from "comps/utils";
1818
import { JSONValue } from "util/jsonTypes";
19+
import React from "react";
20+
import { isEqual } from "lodash";
1921

2022
const ModalStyled = styled.div<{ $background?: string }>`
2123
.ant-modal-content {
@@ -42,23 +44,23 @@ export const SlotConfigContext = createContext<{
4244
modalWidth: 520,
4345
});
4446

45-
const ContainerView = (props: ContainerBaseProps) => {
47+
const ContainerView = React.memo((props: ContainerBaseProps) => {
4648
return <InnerGrid {...props} emptyRows={15} autoHeight />;
47-
};
49+
});
4850

49-
function ModalConfigView(props: {
51+
const ModalConfigView = React.memo((props: {
5052
visible: boolean;
5153
containerProps: ConstructorToView<typeof SimpleContainerComp>;
5254
onCancel: () => void;
53-
}) {
55+
}) => {
5456
const { visible, containerProps, onCancel } = props;
5557
const background = useContext(BackgroundColorContext);
5658
const { modalWidth = 520 } = useContext(SlotConfigContext);
5759
if (!visible) {
5860
return null;
5961
}
6062
return (
61-
(<ModalWrapper>
63+
<ModalWrapper>
6264
<Modal
6365
width={modalWidth}
6466
open={visible}
@@ -67,6 +69,7 @@ function ModalConfigView(props: {
6769
footer={null}
6870
styles={{ body: {padding: "0"} }}
6971
zIndex={Layers.modal}
72+
maskClosable={false}
7073
modalRender={(node) => (
7174
<ModalStyled $background={background} onClick={() => {}}>
7275
{node}
@@ -81,9 +84,9 @@ function ModalConfigView(props: {
8184
items={gridItemCompToGridItems(containerProps.items)}
8285
/>
8386
</Modal>
84-
</ModalWrapper>)
87+
</ModalWrapper>
8588
);
86-
}
89+
}, (prevProps, nextProps) => isEqual(prevProps, nextProps));
8790

8891
const childrenMap = {
8992
container: SimpleContainerComp,

0 commit comments

Comments
 (0)