1
1
import { AppPathParams , AppTypeEnum } from "constants/applicationConstants" ;
2
+ import { ApplicationDSLType } from "constants/applicationConstants" ;
2
3
import { Suspense , lazy , useCallback , useEffect , useMemo , useRef , useState } from "react" ;
3
4
import { useDispatch , useSelector } from "react-redux" ;
4
5
import { useParams } from "react-router" ;
@@ -61,27 +62,18 @@ const AppEditor = React.memo(() => {
61
62
const application = useSelector ( currentApplication ) ;
62
63
const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
63
64
const [ pageSize , setPageSize ] = useState ( 10 ) ;
64
- const [ elements , setElements ] = useState ( { elements : [ ] , total : 1 } )
65
+ const [ elements , setElements ] = useState ( { elements : [ ] , total : 1 } ) ;
65
66
const isLowcoderCompLoading = useSelector ( ( state : AppState ) => state . npmPlugin . loading . lowcoderComps ) ;
66
67
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" )
81
74
? "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 ] ) ;
85
77
86
78
const firstRendered = useRef ( false ) ;
87
79
const orgId = useMemo ( ( ) => currentUser . currentOrgId , [ currentUser . currentOrgId ] ) ;
@@ -90,7 +82,22 @@ const AppEditor = React.memo(() => {
90
82
const [ blockEditing , setBlockEditing ] = useState < boolean > ( false ) ;
91
83
const [ fetchingAppDetails , setFetchingAppDetails ] = useState < boolean > ( false ) ;
92
84
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 ] ) ;
94
101
95
102
if ( ! firstRendered . current ) {
96
103
perfClear ( ) ;
@@ -104,22 +111,32 @@ const AppEditor = React.memo(() => {
104
111
105
112
useUnmount ( ( ) => {
106
113
clearGlobalSettings ( ) ;
114
+ cleanupState ( ) ;
107
115
} ) ;
108
116
109
- // fetch dsl
117
+ // fetch dsl with cleanup
110
118
const [ appInfo , setAppInfo ] = useState < AppSummaryInfo > ( {
111
119
id : "" ,
112
120
appType : AppTypeEnum . Application ,
113
121
} ) ;
114
122
115
- const readOnly = isUserViewMode ;
123
+ const readOnly = selectors . isUserViewMode ;
116
124
const compInstance = useRootCompInstance (
117
125
appInfo ,
118
126
readOnly ,
119
127
isDataSourcePluginRegistered ,
120
128
blockEditing ,
121
129
) ;
122
130
131
+ // Cleanup for compInstance
132
+ useEffect ( ( ) => {
133
+ return ( ) => {
134
+ if ( compInstance ?. comp ) {
135
+ compInstance . comp = null ;
136
+ }
137
+ } ;
138
+ } , [ compInstance ] ) ;
139
+
123
140
useEffect ( ( ) => {
124
141
if ( currentUser && application ) {
125
142
const lastEditedAt = dayjs ( application ?. lastEditedAt ) ;
@@ -129,41 +146,38 @@ const AppEditor = React.memo(() => {
129
146
}
130
147
} , [ application , currentUser ] ) ;
131
148
132
- // fetch dataSource and plugin
149
+ // fetch dataSource and plugin with cleanup
133
150
useEffect ( ( ) => {
134
- if ( ! orgId || paramViewMode !== "edit" ) {
151
+ if ( ! orgId || selectors . paramViewMode !== "edit" ) {
135
152
return ;
136
153
}
137
154
dispatch ( fetchDataSourceTypes ( { organizationId : orgId } ) ) ;
138
155
dispatch ( fetchFolderElements ( { } ) ) ;
139
- } , [ dispatch , orgId , paramViewMode ] ) ;
156
+ } , [ dispatch , orgId , selectors . paramViewMode ] ) ;
140
157
141
158
useEffect ( ( ) => {
142
- if ( applicationId && paramViewMode === "edit" ) {
143
- dispatch ( fetchDataSourceByApp ( { applicationId : applicationId } ) ) ;
159
+ if ( selectors . applicationId && selectors . paramViewMode === "edit" ) {
160
+ dispatch ( fetchDataSourceByApp ( { applicationId : selectors . applicationId } ) ) ;
144
161
dispatch ( fetchQueryLibraryDropdown ( ) ) ;
145
162
}
146
- } , [ dispatch , applicationId , paramViewMode ] ) ;
163
+ } , [ dispatch , selectors . applicationId , selectors . paramViewMode ] ) ;
147
164
148
165
const fetchJSDataSourceByApp = useCallback ( ( ) => {
149
166
fetchJsDSPaginationByApp ( {
150
- appId : applicationId ,
167
+ appId : selectors . applicationId ,
151
168
pageNum : currentPage ,
152
169
pageSize : pageSize
153
170
} ) . then ( ( res ) => {
154
- setElements ( { elements : [ ] , total : res . total || 1 } )
171
+ setElements ( { elements : [ ] , total : res . total || 1 } ) ;
155
172
res . data ! . forEach ( ( i : any ) => {
156
173
registryDataSourcePlugin ( i . type , i . id , i . pluginDefinition ) ;
157
174
} ) ;
158
175
setIsDataSourcePluginRegistered ( true ) ;
176
+ } ) . catch ( ( error ) => {
177
+ setAppError ( error . message || 'Failed to fetch JS data source' ) ;
159
178
} ) ;
160
- dispatch ( setShowAppSnapshot ( false ) ) ;
161
179
} , [
162
- applicationId ,
163
- registryDataSourcePlugin ,
164
- setIsDataSourcePluginRegistered ,
165
- setShowAppSnapshot ,
166
- dispatch ,
180
+ selectors . applicationId ,
167
181
currentPage ,
168
182
pageSize
169
183
] ) ;
@@ -176,10 +190,11 @@ const AppEditor = React.memo(() => {
176
190
177
191
const fetchApplication = useCallback ( ( ) => {
178
192
setFetchingAppDetails ( true ) ;
193
+
179
194
dispatch (
180
195
fetchApplicationInfo ( {
181
- type : viewMode ,
182
- applicationId : applicationId ,
196
+ type : selectors . viewMode as ApplicationDSLType ,
197
+ applicationId : selectors . applicationId ,
183
198
onSuccess : ( info ) => {
184
199
perfMark ( MarkAppDSLLoaded ) ;
185
200
const runJsInHost =
@@ -195,12 +210,12 @@ const AppEditor = React.memo(() => {
195
210
setFetchingAppDetails ( false ) ;
196
211
} ,
197
212
onError : ( errorMessage ) => {
198
- setAppError ( errorMessage ) ;
213
+ setAppError ( errorMessage || 'Failed to fetch application info' ) ;
199
214
setFetchingAppDetails ( false ) ;
200
215
}
201
216
} )
202
217
) ;
203
- } , [ viewMode , applicationId , dispatch , fetchJSDataSourceByApp ] ) ;
218
+ } , [ dispatch , selectors . viewMode , selectors . applicationId , fetchJSDataSourceByApp ] ) ;
204
219
205
220
useEffect ( ( ) => {
206
221
if ( ! isLowcoderCompLoading ) {
@@ -247,7 +262,7 @@ const AppEditor = React.memo(() => {
247
262
< AppSnapshot
248
263
currentAppInfo = { {
249
264
...appInfo ,
250
- dsl : compInstance . comp ?. toJsonValue ( ) || { } ,
265
+ dsl : compInstance ? .comp ?. toJsonValue ( ) || { } ,
251
266
} }
252
267
/>
253
268
</ Suspense >
0 commit comments