Skip to content

Commit 03e9839

Browse files
optimise form components
1 parent b604d43 commit 03e9839

File tree

13 files changed

+552
-251
lines changed

13 files changed

+552
-251
lines changed

client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,16 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => {
397397
);
398398

399399
const dispatchPositionParamsTimerRef = useRef(0);
400+
401+
// Add cleanup for timeout
402+
useEffect(() => {
403+
return () => {
404+
if (dispatchPositionParamsTimerRef.current) {
405+
window.clearTimeout(dispatchPositionParamsTimerRef.current);
406+
}
407+
};
408+
}, []);
409+
400410
const onResize = useCallback(
401411
({width, height}: ResizePayload) => {
402412
if(!width || !height) return;
@@ -444,20 +454,35 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => {
444454
props.dispatch,
445455
]
446456
);
447-
const setSelectedNames = useCallback(
448-
(names: Set<string>) => {
449-
editorState?.setSelectedCompNames(names);
450-
},
451-
[editorState?.setSelectedCompNames]
452-
);
453457

458+
// Cleanup resize detector
454459
const { width, ref } = useResizeDetector({
455460
onResize,
456461
handleHeight: isRowCountLocked,
457462
refreshMode: 'debounce',
458463
refreshRate: 100,
459464
});
460465

466+
const setSelectedNames = useCallback(
467+
(names: Set<string>) => {
468+
editorState?.setSelectedCompNames(names);
469+
},
470+
[editorState?.setSelectedCompNames]
471+
);
472+
473+
// Cleanup item references when items are removed
474+
useEffect(() => {
475+
const currentKeys = new Set(Object.keys(props.items));
476+
const refKeys = new Set(Object.keys(itemViewRef.current));
477+
478+
// Remove references to items that no longer exist
479+
refKeys.forEach(key => {
480+
if (!currentKeys.has(key)) {
481+
delete itemViewRef.current[key];
482+
}
483+
});
484+
}, [props.items]);
485+
461486
const itemViewRef = useRef<GirdItemViewRecord>({});
462487
const itemViews = useMemo(() => {
463488
const newView: GirdItemViewRecord = {};

client/packages/lowcoder/src/comps/comps/formComp/createForm.tsx

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
TacoButton,
1313
} from "lowcoder-design";
1414
import _ from "lodash";
15-
import { useEffect, useState } from "react";
15+
import { useEffect, useState, useCallback } from "react";
1616
import { useDispatch, useSelector } from "react-redux";
1717
import { AppState } from "redux/reducers";
1818
import { fetchDatasourceStructure } from "redux/reduxActions/datasourceActions";
@@ -552,13 +552,22 @@ const CreateFormBody = (props: { onCreate: CreateHandler }) => {
552552
const dataSourceId: string | undefined = Form.useWatch("dataSourceId", form);
553553
const dataSourceItems = useDataSourceItems();
554554
const dataSourceItem = dataSourceItems.find((t) => t.dataSource.id === dataSourceId);
555+
556+
// Cleanup form on unmount
557+
useEffect(() => {
558+
return () => {
559+
form.resetFields();
560+
};
561+
}, [form]);
562+
555563
// default to the first item
556564
useEffect(() => {
557565
if (!dataSourceItem) {
558566
const id = dataSourceItems.length > 0 ? dataSourceItems[0].dataSource.id : undefined;
559567
form.setFieldsValue({ dataSourceId: id });
560568
}
561-
}, [dataSourceItems]);
569+
}, [dataSourceItems, dataSourceItem, form]);
570+
562571
// Refetch when changed
563572
const dispatch = useDispatch();
564573
useEffect(() => {
@@ -570,25 +579,27 @@ const CreateFormBody = (props: { onCreate: CreateHandler }) => {
570579
const tableName: string | undefined = Form.useWatch("tableName", form);
571580
const tableStructures = useTableStructures(dataSourceId);
572581
const tableStructure = tableStructures.find((t) => t.name === tableName);
582+
573583
// default to the first one
574584
useEffect(() => {
575585
if (!tableStructure) {
576586
const name = tableStructures.length > 0 ? tableStructures[0].name : undefined;
577587
form.setFieldsValue({ tableName: name });
578588
}
579-
}, [tableStructures]);
589+
}, [tableStructures, tableStructure, form]);
590+
580591
// Columns of the data table, saved to support drag and drop
581592
const [items, setItems] = useState<RowItem[]>([]);
582593
const dataSourceTypeConfig = dataSourceItem?.typeConfig;
594+
583595
useEffect(() => {
584596
const { initItems, initColumns } = getInitItemsAndColumns(dataSourceTypeConfig, tableStructure);
585597
// Set the initial value by the method. Because if another table has the same column name, setting via initialValue is invalid.
586598
form.setFieldsValue({ columns: initColumns });
587599
setItems(initItems);
588-
}, [dataSourceTypeConfig, tableStructure]);
600+
}, [dataSourceTypeConfig, tableStructure, form]);
589601

590-
const handleDragEnd = (e: { active: { id: string }; over: { id: string } | null }) => {
591-
console.log('handleDragEnd', e);
602+
const handleDragEnd = useCallback((e: { active: { id: string }; over: { id: string } | null }) => {
592603
if (!e.over) {
593604
return;
594605
}
@@ -603,7 +614,7 @@ const CreateFormBody = (props: { onCreate: CreateHandler }) => {
603614
newData.splice(toIndex, 0, movedItem);
604615

605616
setItems(newData);
606-
};
617+
}, [items]);
607618

608619
const emptyText = getEmptyText(dataSourceItems.length, tableStructures.length, items.length);
609620

@@ -688,27 +699,40 @@ const CreateFormBody = (props: { onCreate: CreateHandler }) => {
688699

689700
export const CreateForm = (props: { onCreate: CreateHandler }) => {
690701
const [visible, setVisible] = useState(false);
702+
703+
const handleMouseDown = useCallback((e: React.MouseEvent) => {
704+
setVisible(true);
705+
e.stopPropagation();
706+
}, []);
707+
708+
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
709+
e.stopPropagation();
710+
}, []);
711+
712+
const handleClick = useCallback((e: React.MouseEvent) => {
713+
e.stopPropagation();
714+
}, []);
715+
716+
const handleCancel = useCallback(() => {
717+
setVisible(false);
718+
}, []);
719+
691720
return (
692721
<>
693-
<OpenDialogButton
694-
onMouseDown={(e) => {
695-
setVisible(true);
696-
e.stopPropagation();
697-
}}
698-
>
722+
<OpenDialogButton onMouseDown={handleMouseDown}>
699723
{trans("formComp.openDialogButton")}
700724
</OpenDialogButton>
701725
<div
702-
onKeyDown={(e) => e.stopPropagation()}
703-
onMouseDown={(e) => e.stopPropagation()}
704-
onClick={(e) => e.stopPropagation()}
726+
onKeyDown={handleKeyDown}
727+
onMouseDown={handleMouseDown}
728+
onClick={handleClick}
705729
>
706730
<CustomModal
707731
open={visible}
708732
destroyOnClose={true}
709733
title={trans("formComp.generateForm")}
710734
footer={null}
711-
onCancel={() => setVisible(false)}
735+
onCancel={handleCancel}
712736
width="600px"
713737
children={<CreateFormBody {...props} />}
714738
styles={{ body: {padding: 0} }}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,18 @@ function onCreate(
167167
const BodyPlaceholder = (props: FormProps) => {
168168
const editorState = useContext(EditorContext);
169169
const formName = useContext(CompNameContext);
170+
171+
const handleCreate = (data: CreateData) => {
172+
const result = onCreate(data, props, editorState, formName);
173+
return Promise.resolve(result);
174+
};
175+
170176
return (
171177
<ContainerPlaceholder>
172178
{trans("formComp.containerPlaceholder")}
173179
<br />
174180
<CreateForm
175-
onCreate={(data: CreateData) =>
176-
Promise.resolve(onCreate(data, props, editorState, formName))
177-
}
181+
onCreate={handleCreate}
178182
/>
179183
</ContainerPlaceholder>
180184
);

client/packages/lowcoder/src/comps/comps/formComp/formDataConstants.tsx

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { RecordConstructorToComp } from "lowcoder-core";
22
import { StringControl } from "comps/controls/codeControl";
33
import { CompNameContext, EditorContext } from "comps/editorState";
44
import { Section } from "lowcoder-design";
5-
import { ReactNode } from "react";
5+
import { ReactNode, useContext, useMemo } from "react";
66
import { trans } from "i18n";
77

88
export interface IForm {
@@ -17,24 +17,26 @@ export const formDataChildren = {
1717

1818
type FormDataComp = RecordConstructorToComp<typeof formDataChildren>;
1919

20-
export const FormDataPropertyView = (children: FormDataComp) => (
21-
<EditorContext.Consumer>
22-
{(editorState) => (
23-
<CompNameContext.Consumer>
24-
{(name) => (
25-
<>
26-
{editorState?.findUIParentContainer(name, "form") && (
27-
<Section name={trans("form")}>
28-
{children.formDataKey.propertyView({
29-
label: trans("formComp.name"),
30-
placeholder: name,
31-
tooltip: trans("formComp.nameTooltip"),
32-
})}
33-
</Section>
34-
)}
35-
</>
36-
)}
37-
</CompNameContext.Consumer>
38-
)}
39-
</EditorContext.Consumer>
40-
);
20+
export const FormDataPropertyView = (children: FormDataComp) => {
21+
const editorState = useContext(EditorContext);
22+
const name = useContext(CompNameContext);
23+
24+
const isFormParent = useMemo(() =>
25+
editorState?.findUIParentContainer(name, "form"),
26+
[editorState, name]
27+
);
28+
29+
if (!isFormParent) {
30+
return null;
31+
}
32+
33+
return (
34+
<Section name={trans("form")}>
35+
{children.formDataKey.propertyView({
36+
label: trans("formComp.name"),
37+
placeholder: name,
38+
tooltip: trans("formComp.nameTooltip"),
39+
})}
40+
</Section>
41+
);
42+
};

0 commit comments

Comments
 (0)