11import { AppPathParams , AppTypeEnum } from "constants/applicationConstants" ;
2+ import { ApplicationDSLType } from "constants/applicationConstants" ;
23import { Suspense , lazy , useCallback , useEffect , useMemo , useRef , useState } from "react" ;
34import { useDispatch , useSelector } from "react-redux" ;
45import { 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