Skip to content

Commit dfc5955

Browse files
optimise editor view
1 parent 7dcc4bc commit dfc5955

9 files changed

+633
-287
lines changed

client/packages/lowcoder/src/pages/editor/AppEditor.tsx

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AppPathParams, AppTypeEnum } from "constants/applicationConstants";
2+
import { ApplicationDSLType } from "constants/applicationConstants";
23
import { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
34
import { useDispatch, useSelector } from "react-redux";
45
import { useParams } from "react-router";
@@ -61,27 +62,18 @@ const AppEditor = React.memo(() => {
6162
const application = useSelector(currentApplication);
6263
const [currentPage, setCurrentPage] = useState(1);
6364
const [pageSize, setPageSize] = useState(10);
64-
const [elements, setElements] = useState({ elements: [], total: 1 })
65+
const [elements, setElements] = useState({ elements: [], total: 1 });
6566
const isLowcoderCompLoading = useSelector((state: AppState) => state.npmPlugin.loading.lowcoderComps);
6667

67-
const isUserViewMode = useMemo(
68-
() => params.viewMode ? isUserViewModeCheck : true,
69-
[params.viewMode, isUserViewModeCheck]
70-
);
71-
const applicationId = useMemo(
72-
() => params.applicationId || window.location.pathname.split("/")[2],
73-
[params.applicationId, window.location.pathname]
74-
);
75-
const paramViewMode = useMemo(
76-
() => params.viewMode || window.location.pathname.split("/")[3],
77-
[params.viewMode, window.location.pathname]
78-
);
79-
const viewMode = useMemo(
80-
() => (paramViewMode === "view" || paramViewMode === "admin")
68+
// Memoize selectors to prevent unnecessary re-renders
69+
const selectors = useMemo(() => ({
70+
isUserViewMode: params.viewMode ? isUserViewModeCheck : true,
71+
applicationId: params.applicationId || window.location.pathname.split("/")[2],
72+
paramViewMode: params.viewMode || window.location.pathname.split("/")[3],
73+
viewMode: (params.viewMode === "view" || params.viewMode === "admin")
8174
? "published"
82-
: paramViewMode === "view_marketplace" ? "view_marketplace" : "editing",
83-
[paramViewMode]
84-
);
75+
: params.viewMode === "view_marketplace" ? "view_marketplace" : "editing",
76+
}), [params.viewMode, params.applicationId, window.location.pathname, isUserViewModeCheck]);
8577

8678
const firstRendered = useRef(false);
8779
const orgId = useMemo(() => currentUser.currentOrgId, [currentUser.currentOrgId]);
@@ -90,7 +82,22 @@ const AppEditor = React.memo(() => {
9082
const [blockEditing, setBlockEditing] = useState<boolean>(false);
9183
const [fetchingAppDetails, setFetchingAppDetails] = useState<boolean>(false);
9284

93-
setGlobalSettings({ applicationId, isViewMode: paramViewMode === "view" });
85+
// Cleanup function for state management
86+
const cleanupState = useCallback(() => {
87+
setElements({ elements: [], total: 1 });
88+
setBlockEditing(false);
89+
setFetchingAppDetails(false);
90+
setAppError('');
91+
setIsDataSourcePluginRegistered(false);
92+
}, []);
93+
94+
// Set global settings with cleanup
95+
useEffect(() => {
96+
setGlobalSettings({ applicationId: selectors.applicationId, isViewMode: selectors.paramViewMode === "view" });
97+
return () => {
98+
clearGlobalSettings();
99+
};
100+
}, [selectors.applicationId, selectors.paramViewMode]);
94101

95102
if (!firstRendered.current) {
96103
perfClear();
@@ -104,22 +111,32 @@ const AppEditor = React.memo(() => {
104111

105112
useUnmount(() => {
106113
clearGlobalSettings();
114+
cleanupState();
107115
});
108116

109-
// fetch dsl
117+
// fetch dsl with cleanup
110118
const [appInfo, setAppInfo] = useState<AppSummaryInfo>({
111119
id: "",
112120
appType: AppTypeEnum.Application,
113121
});
114122

115-
const readOnly = isUserViewMode;
123+
const readOnly = selectors.isUserViewMode;
116124
const compInstance = useRootCompInstance(
117125
appInfo,
118126
readOnly,
119127
isDataSourcePluginRegistered,
120128
blockEditing,
121129
);
122130

131+
// Cleanup for compInstance
132+
useEffect(() => {
133+
return () => {
134+
if (compInstance?.comp) {
135+
compInstance.comp = null;
136+
}
137+
};
138+
}, [compInstance]);
139+
123140
useEffect(() => {
124141
if (currentUser && application) {
125142
const lastEditedAt = dayjs(application?.lastEditedAt);
@@ -129,41 +146,38 @@ const AppEditor = React.memo(() => {
129146
}
130147
}, [application, currentUser]);
131148

132-
// fetch dataSource and plugin
149+
// fetch dataSource and plugin with cleanup
133150
useEffect(() => {
134-
if (!orgId || paramViewMode !== "edit") {
151+
if (!orgId || selectors.paramViewMode !== "edit") {
135152
return;
136153
}
137154
dispatch(fetchDataSourceTypes({ organizationId: orgId }));
138155
dispatch(fetchFolderElements({}));
139-
}, [dispatch, orgId, paramViewMode]);
156+
}, [dispatch, orgId, selectors.paramViewMode]);
140157

141158
useEffect(() => {
142-
if (applicationId && paramViewMode === "edit") {
143-
dispatch(fetchDataSourceByApp({ applicationId: applicationId }));
159+
if (selectors.applicationId && selectors.paramViewMode === "edit") {
160+
dispatch(fetchDataSourceByApp({ applicationId: selectors.applicationId }));
144161
dispatch(fetchQueryLibraryDropdown());
145162
}
146-
}, [dispatch, applicationId, paramViewMode]);
163+
}, [dispatch, selectors.applicationId, selectors.paramViewMode]);
147164

148165
const fetchJSDataSourceByApp = useCallback(() => {
149166
fetchJsDSPaginationByApp({
150-
appId: applicationId,
167+
appId: selectors.applicationId,
151168
pageNum: currentPage,
152169
pageSize: pageSize
153170
}).then((res) => {
154-
setElements({elements: [], total: res.total || 1})
171+
setElements({elements: [], total: res.total || 1});
155172
res.data!.forEach((i: any) => {
156173
registryDataSourcePlugin(i.type, i.id, i.pluginDefinition);
157174
});
158175
setIsDataSourcePluginRegistered(true);
176+
}).catch((error) => {
177+
setAppError(error.message || 'Failed to fetch JS data source');
159178
});
160-
dispatch(setShowAppSnapshot(false));
161179
}, [
162-
applicationId,
163-
registryDataSourcePlugin,
164-
setIsDataSourcePluginRegistered,
165-
setShowAppSnapshot,
166-
dispatch,
180+
selectors.applicationId,
167181
currentPage,
168182
pageSize
169183
]);
@@ -176,10 +190,11 @@ const AppEditor = React.memo(() => {
176190

177191
const fetchApplication = useCallback(() => {
178192
setFetchingAppDetails(true);
193+
179194
dispatch(
180195
fetchApplicationInfo({
181-
type: viewMode,
182-
applicationId: applicationId,
196+
type: selectors.viewMode as ApplicationDSLType,
197+
applicationId: selectors.applicationId,
183198
onSuccess: (info) => {
184199
perfMark(MarkAppDSLLoaded);
185200
const runJsInHost =
@@ -195,12 +210,12 @@ const AppEditor = React.memo(() => {
195210
setFetchingAppDetails(false);
196211
},
197212
onError: (errorMessage) => {
198-
setAppError(errorMessage);
213+
setAppError(errorMessage || 'Failed to fetch application info');
199214
setFetchingAppDetails(false);
200215
}
201216
})
202217
);
203-
}, [viewMode, applicationId, dispatch, fetchJSDataSourceByApp]);
218+
}, [dispatch, selectors.viewMode, selectors.applicationId, fetchJSDataSourceByApp]);
204219

205220
useEffect(() => {
206221
if(!isLowcoderCompLoading) {
@@ -247,7 +262,7 @@ const AppEditor = React.memo(() => {
247262
<AppSnapshot
248263
currentAppInfo={{
249264
...appInfo,
250-
dsl: compInstance.comp?.toJsonValue() || {},
265+
dsl: compInstance?.comp?.toJsonValue() || {},
251266
}}
252267
/>
253268
</Suspense>

0 commit comments

Comments
 (0)