From 3f500ab5900e5b34296d78cd450b3d78131a3ade Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Mon, 7 Jul 2025 15:54:08 +0530 Subject: [PATCH 1/6] fix: Removed unwanted state causing the re-renders --- frontend/ee | 2 +- frontend/src/AppBuilder/AppBuilder.jsx | 6 +-- .../src/AppBuilder/AppCanvas/AppCanvas.jsx | 32 ++++-------- .../src/AppBuilder/AppCanvas/Container.jsx | 1 + .../src/AppBuilder/AppCanvas/Grid/Grid.jsx | 2 +- .../src/AppBuilder/AppCanvas/RenderWidget.jsx | 10 ++-- .../AppBuilder/AppCanvas/WidgetWrapper.jsx | 6 ++- .../ComponentManagerTab/DragLayer.jsx | 2 - .../PageMenu/PagesSidebarNavigation.jsx | 2 +- .../AppBuilder/RightSideBar/RightSideBar.jsx | 29 +---------- .../src/AppBuilder/Viewer/MobileHeader.jsx | 2 - .../Viewer/MobileNavigationMenu.jsx | 3 +- frontend/src/AppBuilder/Viewer/Viewer.jsx | 15 +----- frontend/src/AppBuilder/_hooks/useAppData.js | 52 ++++++++----------- .../_stores/slices/rightSideBarSlice.js | 10 ++-- frontend/src/_hooks/useRenderCount.jsx | 4 +- server/ee | 2 +- 17 files changed, 58 insertions(+), 122 deletions(-) diff --git a/frontend/ee b/frontend/ee index 6708f78aa5..3297d43038 160000 --- a/frontend/ee +++ b/frontend/ee @@ -1 +1 @@ -Subproject commit 6708f78aa593343813563ff39ee784e26cd97f1f +Subproject commit 3297d4303806594bd3f5b614df9057c8ceaa92b3 diff --git a/frontend/src/AppBuilder/AppBuilder.jsx b/frontend/src/AppBuilder/AppBuilder.jsx index b300329eef..ab117b2a95 100644 --- a/frontend/src/AppBuilder/AppBuilder.jsx +++ b/frontend/src/AppBuilder/AppBuilder.jsx @@ -1,4 +1,4 @@ -import React, { Suspense, lazy, useEffect } from 'react'; +import React, { Suspense } from 'react'; import useStore from '@/AppBuilder/_stores/store'; import useAppData from '@/AppBuilder/_hooks/useAppData'; import { TJLoader } from '@/_ui/TJLoader/TJLoader'; @@ -28,7 +28,6 @@ import ArtifactPreview from './ArtifactPreview'; // TODO: split Loader into separate component and remove editor loading state from Editor export const Editor = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMode, appType = 'front-end' }) => { useAppData(appId, moduleId, darkMode); - const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen); const isEditorLoading = useStore((state) => state.loaderStore.modules[moduleId].isEditorLoading, shallow); const currentMode = useStore((state) => state.modeStore.modules[moduleId].currentMode, shallow); const isModuleEditor = appType === 'module'; @@ -64,7 +63,6 @@ export const Editor = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod isUserInZeroToOneFlow={isUserInZeroToOneFlow} /> - {isUserInZeroToOneFlow ? ( ) : ( @@ -74,7 +72,7 @@ export const Editor = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod - {isRightSidebarOpen && }{' '} + diff --git a/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx b/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx index b48143f383..28e10a83c7 100644 --- a/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx +++ b/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx @@ -10,7 +10,6 @@ import { shallow } from 'zustand/shallow'; import { computeViewerBackgroundColor, getCanvasWidth } from './appCanvasUtils'; import { NO_OF_GRIDS } from './appCanvasConstants'; import cx from 'classnames'; -import FreezeVersionInfo from '@/AppBuilder/Header/FreezeVersionInfo'; import { computeCanvasContainerHeight } from '../_helpers/editorHelpers'; import AutoComputeMobileLayoutAlert from './AutoComputeMobileLayoutAlert'; import useAppDarkMode from '@/_hooks/useAppDarkMode'; @@ -18,19 +17,17 @@ import useAppCanvasMaxWidth from './useAppCanvasMaxWidth'; import { DeleteWidgetConfirmation } from './DeleteWidgetConfirmation'; import useSidebarMargin from './useSidebarMargin'; import PagesSidebarNavigation from '../RightSideBar/PageSettingsTab/PageMenu/PagesSidebarNavigation'; -import { resolveReferences } from '@/_helpers/utils'; -import useRightSidebarMargin from './userRightSidebarMargin'; import { DragGhostWidget } from './GhostWidgets'; import AppCanvasBanner from '../../AppBuilder/Header/AppCanvasBanner'; -export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode }) => { +export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => { const { moduleId, isModuleMode, appType } = useModuleContext(); const canvasContainerRef = useRef(); const handleCanvasContainerMouseUp = useStore((state) => state.handleCanvasContainerMouseUp, shallow); const canvasHeight = useStore((state) => state.appStore.modules[moduleId].canvasHeight); - const creationMode = useStore((state) => state.appStore.modules[moduleId].app.creationMode); const environmentLoadingState = useStore( - (state) => state.environmentLoadingState || state.loaderStore.modules[moduleId].isEditorLoading + (state) => state.environmentLoadingState || state.loaderStore.modules[moduleId].isEditorLoading, + shallow ); const [canvasWidth, setCanvasWidth] = useState(getCanvasWidth(moduleId)); const gridWidth = canvasWidth / NO_OF_GRIDS; @@ -40,18 +37,14 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode }) const queryPanelHeight = useStore((state) => state?.queryPanel?.queryPanelHeight || 0); const isDraggingQueryPane = useStore((state) => state.queryPanel.isDraggingQueryPane, shallow); const { isAppDarkMode } = useAppDarkMode(); - const canvasBgColor = useStore((state) => state.getCanvasBackgroundColor('canvas', isAppDarkMode), shallow); const canvasContainerHeight = computeCanvasContainerHeight(queryPanelHeight, isDraggingQueryPane); const isAutoMobileLayout = useStore((state) => state.getIsAutoMobileLayout(), shallow); const setIsComponentLayoutReady = useStore((state) => state.setIsComponentLayoutReady, shallow); const canvasMaxWidth = useAppCanvasMaxWidth({ mode: currentMode }); const editorMarginLeft = useSidebarMargin(canvasContainerRef); - // const editorMarginRight = useRightSidebarMargin(canvasContainerRef); - // const isPagesSidebarHidden = useStore((state) => state.getPagesSidebarVisibility('canvas'), shallow); - const isSidebarOpen = useStore((state) => state.isSidebarOpen, shallow); const getPageId = useStore((state) => state.getCurrentPageId, shallow); const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen, shallow); - const isRightSidebarPinned = useStore((state) => state.isRightSidebarPinned, shallow); + const isSidebarOpen = useStore((state) => state.isSidebarOpen, shallow); const currentPageId = useStore((state) => state.modules[moduleId].currentPageId); const homePageId = useStore((state) => state.appStore.modules[moduleId].app.homePageId); @@ -59,10 +52,9 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode }) localStorage.getItem('isPagesSidebarPinned') !== 'false' ); - const { globalSettings, pages, pageSettings, switchPage } = useStore( + const { globalSettings, pageSettings, switchPage } = useStore( (state) => ({ globalSettings: state.globalSettings, - pages: state.modules.canvas.pages, pageSettings: state.pageSettings, switchPage: state.switchPage, }), @@ -70,11 +62,8 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode }) ); const showHeader = !globalSettings?.hideHeader; - const { definition: { styles = {}, properties = {} } = {} } = pageSettings ?? {}; - const { position, disableMenu, showOnDesktop } = properties ?? {}; - const isPagesSidebarHidden = resolveReferences(disableMenu?.value); - - const hideSidebar = isModuleMode || isPagesSidebarHidden || appType === 'module'; + const { definition: { properties = {} } = {} } = pageSettings ?? {}; + const { position, showOnDesktop } = properties ?? {}; useEffect(() => { // Need to remove this if we shift setExposedVariable Logic outside of components @@ -109,8 +98,6 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode }) return () => window.removeEventListener('resize', handleResize); }, [currentLayout, canvasMaxWidth, isViewerSidebarPinned, moduleId, isRightSidebarOpen]); - useEffect(() => {}, [isViewerSidebarPinned]); - const canvasContainerStyles = useMemo(() => { const canvasBgColor = currentMode === 'view' @@ -155,9 +142,9 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode }) let offset; if (isViewerSidebarPinned) { - offset = position === 'side' ? '352px' : '126px'; + offset = position === 'side' ? '352px' : '127px'; } else { - offset = position === 'side' ? '171px' : '126px'; + offset = position === 'side' ? '171px' : '127px'; } return `calc(100vw - ${offset})`; @@ -186,7 +173,6 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode }) ))} diff --git a/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx b/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx index dbe9ed941d..addd47f6c6 100644 --- a/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx +++ b/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx @@ -871,7 +871,6 @@ export default function Grid({ gridWidth, currentLayout }) { if (getHoveredComponentForGrid() !== e.target.id) { return false; } - toggleRightSidebar(); newDragParentId.current = boxList.find((box) => box.id === e.target.id)?.parent; e?.moveable?.controlBox?.removeAttribute('data-off-screen'); @@ -983,6 +982,7 @@ export default function Grid({ gridWidth, currentLayout }) { useStore.getState().setDraggingComponentId(e.target.id); showGridLines(); isDraggingRef.current = true; + toggleRightSidebar(); } const currentWidget = boxList.find((box) => box.id === e.target.id); const currentParentId = diff --git a/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx b/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx index 0409d22d77..0099337e37 100644 --- a/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx +++ b/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx @@ -7,7 +7,6 @@ import { renderTooltip } from '@/_helpers/appUtils'; import { useTranslation } from 'react-i18next'; import ErrorBoundary from '@/_ui/ErrorBoundary'; import { BOX_PADDING } from './appCanvasConstants'; -import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext'; const SHOULD_ADD_BOX_SHADOW_AND_VISIBILITY = [ 'Table', @@ -48,13 +47,12 @@ const RenderWidget = ({ widgetWidth, inCanvas = false, darkMode, + moduleId, }) => { - const { moduleId } = useModuleContext(); - const componentDefinition = useStore((state) => state.getComponentDefinition(id, moduleId), shallow); + const component = useStore((state) => state.getComponentDefinition(id, moduleId)?.component, shallow); const getDefaultStyles = useStore((state) => state.debugger.getDefaultStyles, shallow); const adjustComponentPositions = useStore((state) => state.adjustComponentPositions, shallow); const componentCount = useStore((state) => state.getContainerChildrenMapping(id)?.length || 0, shallow); - const component = componentDefinition?.component; const componentName = component?.name; const [key, setKey] = useState(Math.random()); const resolvedProperties = useStore( @@ -74,7 +72,7 @@ const RenderWidget = ({ (state) => state.getResolvedComponent(id, subContainerIndex, moduleId)?.generalStyles, shallow ); - const unResolvedValidation = componentDefinition?.component?.definition?.validation || {}; + const unResolvedValidation = component?.definition?.validation || {}; // const others = useStore((state) => state.getResolvedComponent(id, subContainerIndex)?.others, shallow); const updateDependencyValues = useStore((state) => state.updateDependencyValues, shallow); const validateWidget = useStore((state) => state.validateWidget, shallow); @@ -153,7 +151,7 @@ const RenderWidget = ({ useEffect(() => { setExposedVariable('id', id); }, []); - if (!componentDefinition?.component) return null; + if (!component) return null; const disabledState = resolvedProperties?.disabledState; const loadingState = resolvedProperties?.loadingState; diff --git a/frontend/src/AppBuilder/AppCanvas/WidgetWrapper.jsx b/frontend/src/AppBuilder/AppCanvas/WidgetWrapper.jsx index 057781f8ec..2616691311 100644 --- a/frontend/src/AppBuilder/AppCanvas/WidgetWrapper.jsx +++ b/frontend/src/AppBuilder/AppCanvas/WidgetWrapper.jsx @@ -6,7 +6,6 @@ import { ConfigHandle } from './ConfigHandle/ConfigHandle'; import { useGridStore } from '@/_stores/gridStore'; import cx from 'classnames'; import RenderWidget from './RenderWidget'; -import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext'; import { NO_OF_GRIDS } from './appCanvasConstants'; const WidgetWrapper = memo( @@ -21,8 +20,8 @@ const WidgetWrapper = memo( readOnly, mode, darkMode, + moduleId, }) => { - const { moduleId } = useModuleContext(); const calculateMoveableBoxHeightWithId = useStore((state) => state.calculateMoveableBoxHeightWithId, shallow); const stylesDefinition = useStore( (state) => state.getComponentDefinition(id, moduleId)?.component?.definition?.styles, @@ -127,6 +126,7 @@ const WidgetWrapper = memo( onOptionChange={onOptionChange} darkMode={darkMode} onOptionsChange={onOptionsChange} + moduleId={moduleId} /> @@ -135,4 +135,6 @@ const WidgetWrapper = memo( } ); +WidgetWrapper.displayName = 'WidgetWrapper'; + export default WidgetWrapper; diff --git a/frontend/src/AppBuilder/RightSideBar/ComponentManagerTab/DragLayer.jsx b/frontend/src/AppBuilder/RightSideBar/ComponentManagerTab/DragLayer.jsx index 16b8a74f77..5b12ff0f9b 100644 --- a/frontend/src/AppBuilder/RightSideBar/ComponentManagerTab/DragLayer.jsx +++ b/frontend/src/AppBuilder/RightSideBar/ComponentManagerTab/DragLayer.jsx @@ -37,8 +37,6 @@ export const DragLayer = ({ index, component, isModuleTab = false, disabled = fa toggleRightSidebar(!isRightSidebarOpen); } setShowModuleBorder(true); - } else { - setShowModuleBorder(false); } }, [isDragging, setShowModuleBorder, isModuleEditor, toggleRightSidebar]); diff --git a/frontend/src/AppBuilder/RightSideBar/PageSettingsTab/PageMenu/PagesSidebarNavigation.jsx b/frontend/src/AppBuilder/RightSideBar/PageSettingsTab/PageMenu/PagesSidebarNavigation.jsx index 03f129d32c..ec4710d2d9 100644 --- a/frontend/src/AppBuilder/RightSideBar/PageSettingsTab/PageMenu/PagesSidebarNavigation.jsx +++ b/frontend/src/AppBuilder/RightSideBar/PageSettingsTab/PageMenu/PagesSidebarNavigation.jsx @@ -21,7 +21,6 @@ import { Overlay, Popover } from 'react-bootstrap'; export const PagesSidebarNavigation = ({ isMobileDevice, - pages, currentPageId, switchPage, darkMode, @@ -42,6 +41,7 @@ export const PagesSidebarNavigation = ({ const appName = useStore((state) => state.appStore.modules[moduleId].app.appName); const isSidebarOpen = useStore((state) => state.isSidebarOpen); const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen, shallow); + const pages = useStore((state) => state.modules.canvas.pages, shallow); const navRef = useRef(null); const moreRef = useRef(null); diff --git a/frontend/src/AppBuilder/RightSideBar/RightSideBar.jsx b/frontend/src/AppBuilder/RightSideBar/RightSideBar.jsx index cec562ac13..760f161433 100644 --- a/frontend/src/AppBuilder/RightSideBar/RightSideBar.jsx +++ b/frontend/src/AppBuilder/RightSideBar/RightSideBar.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React from 'react'; import useStore from '@/AppBuilder/_stores/store'; import { ComponentConfigurationTab } from './ComponentConfigurationTab'; import ComponentsManagerTab from './ComponentManagerTab'; @@ -10,36 +10,11 @@ export const RightSideBar = ({ darkMode }) => { const { isModuleEditor } = useModuleContext(); const activeTab = useStore((state) => state.activeRightSideBarTab); const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen); - const setRightSidebarOpen = useStore((state) => state.setRightSidebarOpen); - const isRightSidebarPinned = useStore((state) => state.isRightSidebarPinned); - const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab); - - const sidebarRef = useRef(null); - - // useEffect(() => { - // const rigthSidebarMenu = document.querySelector('.right-sidebar-toggle'); - // function handleClickOutside(event) { - // if ( - // sidebarRef.current && - // !sidebarRef.current.contains(event.target) && - // !rigthSidebarMenu.contains(event.target) && - // !isRightSidebarPinned - // ) { - // setRightSidebarOpen(false); - // setActiveRightSideBarTab(null); - // } - // } - - // document.addEventListener('mousedown', handleClickOutside); - // return () => { - // document.removeEventListener('mousedown', handleClickOutside); - // }; - // }, [isRightSidebarPinned, setActiveRightSideBarTab, setRightSidebarOpen]); if (!isRightSidebarOpen) return null; return ( -
+
{activeTab === 'pages' && } diff --git a/frontend/src/AppBuilder/Viewer/MobileHeader.jsx b/frontend/src/AppBuilder/Viewer/MobileHeader.jsx index 100b4572b1..fcc0cb6f13 100644 --- a/frontend/src/AppBuilder/Viewer/MobileHeader.jsx +++ b/frontend/src/AppBuilder/Viewer/MobileHeader.jsx @@ -18,7 +18,6 @@ const MobileHeader = ({ appName, changeToDarkMode, darkMode, - pages, currentPageId, switchPage, setAppDefinitionFromVersion, @@ -61,7 +60,6 @@ const MobileHeader = ({ const _renderMobileNavigationMenu = () => ( { +const RenderGroup = ({ pageGroup, currentPage, darkMode, handlepageSwitch, currentPageId, icon }) => { const { moduleId } = useModuleContext(); const [isExpanded, setIsExpanded] = useState(true); const groupActive = currentPage.pageGroupId === pageGroup?.id; const homePageId = useStore((state) => state.appStore.modules[moduleId].app.homePageId); + const pages = useStore((state) => state.modules[moduleId].pages); const handleToggle = () => { setIsExpanded(!isExpanded); }; diff --git a/frontend/src/AppBuilder/Viewer/Viewer.jsx b/frontend/src/AppBuilder/Viewer/Viewer.jsx index a9b8cdc856..a8878acb43 100644 --- a/frontend/src/AppBuilder/Viewer/Viewer.jsx +++ b/frontend/src/AppBuilder/Viewer/Viewer.jsx @@ -16,7 +16,6 @@ import Popups from '../Popups'; import { ModuleProvider } from '@/AppBuilder/_contexts/ModuleContext'; import { getPatToken, setPatToken } from '@/AppBuilder/EmbedApp'; import Spinner from '@/_ui/Spinner'; -import toast from 'react-hot-toast'; export const Viewer = ({ id: appId, @@ -41,7 +40,6 @@ export const Viewer = ({ currentCanvasWidth, currentPageId, globalSettings, - pages, pageSettings, updateCanvasHeight, appName, @@ -62,9 +60,6 @@ export const Viewer = ({ homePageId: state.appStore.modules[moduleId].app.homepageId, currentPageId: state.modules[moduleId].currentPageId, globalSettings: state.globalSettings, - pages: state.modules[moduleId].pages, - modules: state.modules, - globalSettingsChanged: state.globalSettingsChanged, pageSettings: state.pageSettings, updateCanvasHeight: state.updateCanvasBottomHeight, isMaintenanceOn: state.appStore.modules[moduleId].app.isMaintenanceOn, @@ -77,7 +72,6 @@ export const Viewer = ({ const getCurrentPageComponents = useStore((state) => state.getCurrentPageComponents(moduleId), shallow); const currentPageComponents = useMemo(() => getCurrentPageComponents, [getCurrentPageComponents]); - const changeDarkMode = useStore((state) => state.changeDarkMode); const isPagesSidebarHidden = useStore((state) => state.getPagesSidebarVisibility('canvas'), shallow); const canvasBgColor = useStore((state) => state.getCanvasBackgroundColor('canvas', darkMode), shallow); const deviceWindowWidth = window.screen.width - 5; @@ -104,18 +98,14 @@ export const Viewer = ({ }, [isSidebarPinned]); const { definition: { properties = {} } = {} } = pageSettings ?? {}; - const { position, hideHeader } = properties ?? {}; + const { position } = properties ?? {}; const canvasRef = useRef(null); - const isLoading = false; const isMobilePreviewMode = selectedVersion?.id && currentLayout === 'mobile'; const isAppLoaded = !!editingVersion; - const isMobileDevice = deviceWindowWidth < 600; const switchPage = useStore((state) => state.switchPage); const showHeader = !globalSettings?.hideHeader && isAppLoaded; - const isLicenseValid = useStore((state) => state.isLicenseValid); - const licenseValid = isLicenseValid(); // ---remove const handleAppEnvironmentChanged = useCallback((environment) => { console.log('setAppVersionCurrentEnvironment', environment); @@ -160,7 +150,6 @@ export const Viewer = ({ isAppLoaded={isAppLoaded} appName={appName} darkMode={darkMode} - pages={pages} currentPageId={currentPageId ?? homePageId} showViewerNavigation={!hideSidebar} handleAppEnvironmentChanged={handleAppEnvironmentChanged} @@ -176,7 +165,6 @@ export const Viewer = ({ showHeader={showHeader} appName={appName} darkMode={darkMode} - pages={pages} currentPageId={currentPageId ?? homePageId} showViewerNavigation={!hideSidebar} handleAppEnvironmentChanged={handleAppEnvironmentChanged} @@ -273,7 +261,6 @@ export const Viewer = ({ showHeader={showHeader && isAppLoaded} appName={appName} darkMode={darkMode} - pages={pages} currentPageId={currentPageId ?? homePageId} showViewerNavigation={!hideSidebar} handleAppEnvironmentChanged={handleAppEnvironmentChanged} diff --git a/frontend/src/AppBuilder/_hooks/useAppData.js b/frontend/src/AppBuilder/_hooks/useAppData.js index a7ce7e0e3d..2bf59644b8 100644 --- a/frontend/src/AppBuilder/_hooks/useAppData.js +++ b/frontend/src/AppBuilder/_hooks/useAppData.js @@ -5,21 +5,17 @@ import { appsService, appVersionService, dataqueryService, - datasourceService, orgEnvironmentConstantService, authenticationService, - orgEnvironmentVariableService, customStylesService, } from '@/_services'; import useStore from '@/AppBuilder/_stores/store'; -import { useEnvironmentsAndVersionsStore } from '@/_stores/environmentsAndVersionsStore'; -import { camelCase, cloneDeep, isEmpty, kebabCase, mapKeys, noop, rest } from 'lodash'; +import { camelCase, isEmpty, mapKeys, noop } from 'lodash'; import { usePrevious } from '@dnd-kit/utilities'; import { deepCamelCase } from '@/_helpers/appUtils'; import { useEventActions } from '../_stores/slices/eventsSlice'; import useRouter from '@/_hooks/use-router'; -import { extractEnvironmentConstantsFromConstantsList, navigate } from '../_utils/misc'; -import { getWorkspaceId } from '@/_helpers/utils'; +import { extractEnvironmentConstantsFromConstantsList } from '../_utils/misc'; import { shallow } from 'zustand/shallow'; import { fetchAndSetWindowTitle, pageTitles, retrieveWhiteLabelText } from '@white-label/whiteLabelling'; import { initEditorWalkThrough } from '@/AppBuilder/_helpers/createWalkThrough'; @@ -30,7 +26,6 @@ import { getPreviewQueryParams } from '@/_helpers/routes'; import { useLocation, useMatch, useParams } from 'react-router-dom'; import { useMounted } from '@/_hooks/use-mount'; import useThemeAccess from './useThemeAccess'; -import { handleError } from '@/_helpers/handleAppAccess'; import toast from 'react-hot-toast'; /** @@ -67,13 +62,13 @@ const useAppData = ( moduleMode = false ) => { const mounted = useMounted(); - const initModules = useStore((state) => state.initModules, shallow); + const initModules = useStore((state) => state.initModules); moduleMode && !mounted && initModules(moduleId); const { state } = useLocation(); const [currentSession, setCurrentSession] = useState(); + const setEditorLoading = useStore((state) => state.setEditorLoading); const setApp = useStore((state) => state.setApp); - const app = useStore((state) => state.appStore.modules[moduleId].app); const user = useStore((state) => state.user); const setCurrentVersionId = useStore((state) => state.setCurrentVersionId); const currentVersionId = useStore((state) => state.currentVersionId); @@ -90,10 +85,6 @@ const useAppData = ( const setPreviewData = useStore((state) => state.queryPanel.setPreviewData); // const fetchDataSources = useStore((state) => state.fetchDataSources); const fetchGlobalDataSources = useStore((state) => state.fetchGlobalDataSources); - const previousVersion = usePrevious(currentVersionId); - const events = useStore((state) => state.eventsSlice.module[moduleId]?.events || []); - const pages = useStore((state) => state.modules[moduleId]?.pages || []); - const currentPageId = useStore((state) => state.modules[moduleId].currentPageId); const setResolvedConstants = useStore((state) => state.setResolvedConstants); const setSecrets = useStore((state) => state.setSecrets); const setQueryMapping = useStore((state) => state.setQueryMapping); @@ -115,23 +106,17 @@ const useAppData = ( const cleanUpStore = useStore((state) => state.cleanUpStore); const selectedEnvironment = useStore((state) => state.selectedEnvironment); const setIsEditorFreezed = useStore((state) => state.setIsEditorFreezed); - const appMode = useStore((state) => state.globalSettings.appMode); - const selectedTheme = useStore((state) => state.globalSettings.theme); - const previousEnvironmentId = usePrevious(selectedEnvironment?.id); - const isComponentLayoutReady = useStore((state) => state.appStore.modules[moduleId].isComponentLayoutReady, shallow); - const pageSwitchInProgress = useStore((state) => state.pageSwitchInProgress); const setPageSwitchInProgress = useStore((state) => state.setPageSwitchInProgress); const selectedVersion = useStore((state) => state.selectedVersion); const setIsPublicAccess = useStore((state) => state.setIsPublicAccess); - const setModulesIsLoading = useStore((state) => state?.setModulesIsLoading ?? noop, shallow); - const setModulesList = useStore((state) => state?.setModulesList ?? noop, shallow); + const setModulesIsLoading = useStore((state) => state?.setModulesIsLoading ?? noop); + const setModulesList = useStore((state) => state?.setModulesList ?? noop); const setModuleDefinition = useStore((state) => state?.setModuleDefinition ?? noop); const getModuleDefinition = useStore((state) => state?.getModuleDefinition ?? noop); const deleteModuleDefinition = useStore((state) => state?.deleteModuleDefinition ?? noop); const themeAccess = useThemeAccess(); - const themeChanged = useStore((state) => state.themeChanged); const detectThemeChange = useStore((state) => state.detectThemeChange); const setConversation = useStore((state) => state.ai?.setConversation); const setDocsConversation = useStore((state) => state.ai?.setDocsConversation); @@ -142,10 +127,18 @@ const useAppData = ( const toggleLeftSidebar = useStore((state) => state.toggleLeftSidebar); const pathParams = useParams(); const slug = moduleMode ? '' : pathParams?.slug; + + const previousVersion = usePrevious(currentVersionId); + const events = useStore((state) => state.eventsSlice.module[moduleId]?.events || []); + const currentPageId = useStore((state) => state.modules[moduleId].currentPageId); + const appMode = useStore((state) => state.globalSettings.appMode); + const selectedTheme = useStore((state) => state.globalSettings.theme); + const previousEnvironmentId = usePrevious(selectedEnvironment?.id); + const isComponentLayoutReady = useStore((state) => state.appStore.modules[moduleId].isComponentLayoutReady); + const pageSwitchInProgress = useStore((state) => state.pageSwitchInProgress); const licenseStatus = useStore((state) => state.isLicenseValid()); - - - const match = useMatch('/applications/:slug/:pageHandle'); + const organizationId = useStore((state) => state.appStore.modules[moduleId].app.organizationId); + const appName = useStore((state) => state.appStore.modules[moduleId].app.name); const location = useRouter().location; @@ -543,10 +536,7 @@ const useAppData = ( useEffect(() => { if (isComponentLayoutReady) { runOnLoadQueries(moduleId).then(() => { - let startingPage = pages.find((page) => page.id === currentPageId); - const currentPageEvents = events.filter( - (event) => event.target === 'page' && event.sourceId === startingPage.id - ); + const currentPageEvents = events.filter((event) => event.target === 'page' && event.sourceId === currentPageId); handleEvent('onPageLoad', currentPageEvents, {}); }); } @@ -556,12 +546,12 @@ const useAppData = ( if (moduleId) return; fetchAndSetWindowTitle({ page: pageTitles.EDITOR, - appName: app.appName, + appName: appName, mode: mode, isReleased: isReleasedVersionId, licenseStatus: licenseStatus, }); - }, [app.appName, isReleasedVersionId, licenseStatus, mode, moduleId]); + }, [appName, isReleasedVersionId, licenseStatus, mode, moduleId]); useEffect(() => { const root = document.documentElement; @@ -656,7 +646,7 @@ const useAppData = ( } }); // fetchDataSources(currentVersionId, selectedEnvironment.id); - fetchGlobalDataSources(app.organizationId, currentVersionId, selectedEnvironment.id); + fetchGlobalDataSources(organizationId, currentVersionId, selectedEnvironment.id); setResolvedConstants(orgConstants); setSecrets(orgSecrets); } diff --git a/frontend/src/AppBuilder/_stores/slices/rightSideBarSlice.js b/frontend/src/AppBuilder/_stores/slices/rightSideBarSlice.js index 027c29ca70..5831041dd8 100644 --- a/frontend/src/AppBuilder/_stores/slices/rightSideBarSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/rightSideBarSlice.js @@ -6,10 +6,12 @@ const initialState = { isRightSidebarPinned: false, }; -export const createRightSideBarSlice = (set) => ({ +export const createRightSideBarSlice = (set, get) => ({ ...initialState, setActiveRightSideBarTab: (tab) => set(() => ({ activeRightSideBarTab: tab }), false, 'setActiveRightSideBarTab'), - toggleRightSidebar: () => set((state) => ({ isRightSidebarOpen: !state.isRightSidebarOpen })), - setRightSidebarOpen: (open) => set(() => ({ isRightSidebarOpen: open })), - toggleRightSidebarPin: () => set((state) => ({ isRightSidebarPinned: !state.isRightSidebarPinned })), + toggleRightSidebar: () => + set((state) => ({ isRightSidebarOpen: !state.isRightSidebarOpen }), false, 'toggleRightSidebar'), + setRightSidebarOpen: (open) => set(() => ({ isRightSidebarOpen: open }), false, 'setRightSidebarOpen'), + toggleRightSidebarPin: () => + set((state) => ({ isRightSidebarPinned: !state.isRightSidebarPinned }), false, 'toggleRightSidebarPin'), }); diff --git a/frontend/src/_hooks/useRenderCount.jsx b/frontend/src/_hooks/useRenderCount.jsx index a56c42f985..26dfd9d8ec 100644 --- a/frontend/src/_hooks/useRenderCount.jsx +++ b/frontend/src/_hooks/useRenderCount.jsx @@ -1,12 +1,12 @@ import { useRef, useEffect } from 'react'; -function useRenderCount(componentName) { +function useRenderCount(componentName, options = {}) { const renderCountRef = useRef(0); renderCountRef.current++; useEffect(() => { - console.log(`${componentName} rendered: ${renderCountRef.current} times`); + console.log(`here--- ${componentName} rendered: ${renderCountRef.current} times `, options); // eslint-disable-next-line react-hooks/exhaustive-deps }, [renderCountRef.current, componentName]); diff --git a/server/ee b/server/ee index dcb4173b2d..cc864000dd 160000 --- a/server/ee +++ b/server/ee @@ -1 +1 @@ -Subproject commit dcb4173b2db2a6ee3d96b21e0b32f2f0b0f2c4b0 +Subproject commit cc864000dd03cc345e53ae9fc43821d3174f4c64 From 9468466ff035738bc116fd6f854866f0113cb2f2 Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Mon, 7 Jul 2025 15:54:27 +0530 Subject: [PATCH 2/6] chore: Updated submodules ref --- frontend/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ee b/frontend/ee index 3297d43038..bbb1824874 160000 --- a/frontend/ee +++ b/frontend/ee @@ -1 +1 @@ -Subproject commit 3297d4303806594bd3f5b614df9057c8ceaa92b3 +Subproject commit bbb18248742c917494032166b7948b7e85750583 From 68fa104582b3923d3ad9586a5f0d66bab6066733 Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Mon, 7 Jul 2025 20:17:17 +0530 Subject: [PATCH 3/6] fix: fixed the unwanted re-renders on modules --- frontend/ee | 2 +- .../src/AppBuilder/AppCanvas/AppCanvas.jsx | 13 +++++++-- .../src/AppBuilder/AppCanvas/Container.jsx | 8 ++++-- .../src/AppBuilder/AppCanvas/RenderWidget.jsx | 3 ++ .../AppBuilder/_hooks/useSortedComponents.js | 28 +++++++++++++------ 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/frontend/ee b/frontend/ee index bbb1824874..b8c4bdc50b 160000 --- a/frontend/ee +++ b/frontend/ee @@ -1 +1 @@ -Subproject commit bbb18248742c917494032166b7948b7e85750583 +Subproject commit b8c4bdc50b7d38a10b96559823d01200d72669d0 diff --git a/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx b/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx index 28e10a83c7..b121821370 100644 --- a/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx +++ b/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx @@ -19,6 +19,7 @@ import useSidebarMargin from './useSidebarMargin'; import PagesSidebarNavigation from '../RightSideBar/PageSettingsTab/PageMenu/PagesSidebarNavigation'; import { DragGhostWidget } from './GhostWidgets'; import AppCanvasBanner from '../../AppBuilder/Header/AppCanvasBanner'; +import { debounce } from 'lodash'; export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => { const { moduleId, isModuleMode, appType } = useModuleContext(); @@ -73,7 +74,7 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => { }, []); useEffect(() => { - function handleResize() { + function handleResizeImmediate() { const _canvasWidth = moduleId === 'canvas' ? document.getElementById('real-canvas')?.getBoundingClientRect()?.width @@ -81,6 +82,8 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => { if (_canvasWidth !== 0) setCanvasWidth(_canvasWidth); } + const handleResize = debounce(handleResizeImmediate, 300); + if (moduleId === 'canvas') { window.addEventListener('resize', handleResize); } else { @@ -91,11 +94,15 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => { return () => { if (elem) resizeObserver.unobserve(elem); resizeObserver.disconnect(); + handleResize.cancel(); }; } - handleResize(); + handleResizeImmediate(); - return () => window.removeEventListener('resize', handleResize); + return () => { + window.removeEventListener('resize', handleResize); + handleResize.cancel(); + }; }, [currentLayout, canvasMaxWidth, isViewerSidebarPinned, moduleId, isRightSidebarOpen]); const canvasContainerStyles = useMemo(() => { diff --git a/frontend/src/AppBuilder/AppCanvas/Container.jsx b/frontend/src/AppBuilder/AppCanvas/Container.jsx index 8479bf41b4..c2aebc1eb5 100644 --- a/frontend/src/AppBuilder/AppCanvas/Container.jsx +++ b/frontend/src/AppBuilder/AppCanvas/Container.jsx @@ -37,7 +37,7 @@ import { noop } from 'lodash'; index - used to identify the subcontainer index onOptionChange - used to pass the onOptionChange function to the child components and pass the exposedValues to the parent component */ -export const Container = React.memo( +const Container = React.memo( ({ id, canvasWidth, @@ -235,7 +235,7 @@ export const Container = React.memo(
); }; - const sortedComponents = useSortedComponents(components, currentLayout, id); + const sortedComponents = useSortedComponents(components, currentLayout, id, moduleId); return (
); }; + +RenderWidget.displayName = 'RenderWidget'; + export default memo(RenderWidget); diff --git a/frontend/src/AppBuilder/_hooks/useSortedComponents.js b/frontend/src/AppBuilder/_hooks/useSortedComponents.js index 9877206444..7687a8622d 100644 --- a/frontend/src/AppBuilder/_hooks/useSortedComponents.js +++ b/frontend/src/AppBuilder/_hooks/useSortedComponents.js @@ -2,23 +2,35 @@ import { useMemo, useRef } from 'react'; import useStore from '@/AppBuilder/_stores/store'; import { shallow } from 'zustand/shallow'; -const useSortedComponents = (components, currentLayout, id) => { +const useSortedComponents = (components, currentLayout, id, moduleId) => { const getCurrentPageComponents = useStore((state) => state.getCurrentPageComponents, shallow); - const reorderContainerChildren = useStore((state) => state.reorderContainerChildren, shallow); + // Only subscribe to reorderContainerChildren when it's relevant to this specific container + const reorderContainerChildren = useStore((state) => { + const { containerId, triggerUpdate } = state.reorderContainerChildren; + // Only return an updated trigger when this specific container is being reordered + // Return a stable value for other containers to prevent unnecessary re-renders + if (containerId === id && moduleId === 'canvas') { + return { triggerUpdate, containerId, shouldReorder: true }; + } + return { triggerUpdate: 0, containerId: null, shouldReorder: false }; + }, shallow); + const prevForceUpdateRef = useRef(0); const prevComponentsOrder = useRef(components); // Function to sort the components based on position in container for tab navigation const sortedComponents = useMemo(() => { - const { triggerUpdate, containerId } = reorderContainerChildren; + const { triggerUpdate, shouldReorder } = reorderContainerChildren; - // If a forced update occurred for a different container, return the previous order + // If this container is not the target of the reorder, return cached order + if (!shouldReorder) { + return prevComponentsOrder.current; + } + + // If a forced update occurred for this container, recalculate order const isForcedUpdate = prevForceUpdateRef.current !== triggerUpdate; if (isForcedUpdate) { prevForceUpdateRef.current = triggerUpdate; - if (containerId !== id) { - return prevComponentsOrder.current; - } } const currentPageComponents = getCurrentPageComponents(); @@ -41,7 +53,7 @@ const useSortedComponents = (components, currentLayout, id) => { prevComponentsOrder.current = newComponentsOrder; return newComponentsOrder; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [components, currentLayout, reorderContainerChildren.triggerUpdate, id]); + }, [components, currentLayout, reorderContainerChildren.triggerUpdate, reorderContainerChildren.shouldReorder]); return sortedComponents; }; From d931b7ecc6a67e05f2891dff941156cc6e4ee47c Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Wed, 9 Jul 2025 12:22:28 +0530 Subject: [PATCH 4/6] fix: optimize component sorting logic to ensure recalculation on changes. Fixes the bug on showing the component from one subcontainer to another --- .../src/AppBuilder/_hooks/useSortedComponents.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/frontend/src/AppBuilder/_hooks/useSortedComponents.js b/frontend/src/AppBuilder/_hooks/useSortedComponents.js index 7687a8622d..f89328ead0 100644 --- a/frontend/src/AppBuilder/_hooks/useSortedComponents.js +++ b/frontend/src/AppBuilder/_hooks/useSortedComponents.js @@ -22,10 +22,10 @@ const useSortedComponents = (components, currentLayout, id, moduleId) => { const sortedComponents = useMemo(() => { const { triggerUpdate, shouldReorder } = reorderContainerChildren; - // If this container is not the target of the reorder, return cached order - if (!shouldReorder) { - return prevComponentsOrder.current; - } + // Always recalculate if components array has changed (new component added/removed) + const componentsChanged = + prevComponentsOrder.current.length !== components.length || + !components.every((comp) => prevComponentsOrder.current.includes(comp)); // If a forced update occurred for this container, recalculate order const isForcedUpdate = prevForceUpdateRef.current !== triggerUpdate; @@ -33,6 +33,14 @@ const useSortedComponents = (components, currentLayout, id, moduleId) => { prevForceUpdateRef.current = triggerUpdate; } + // Skip recalculation only if: + // 1. This container is not the target of reorder + // 2. Components haven't changed + // 3. No forced update occurred + if (!shouldReorder && !componentsChanged && !isForcedUpdate) { + return prevComponentsOrder.current; + } + const currentPageComponents = getCurrentPageComponents(); const newComponentsOrder = [...components].sort((a, b) => { From c0687dca492c562baa9f26ddefcb61c5412385f3 Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Wed, 9 Jul 2025 14:03:43 +0530 Subject: [PATCH 5/6] Update EE frontend and server submodules --- frontend/ee | 2 +- server/ee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/ee b/frontend/ee index b8c4bdc50b..c5d9ed35fc 160000 --- a/frontend/ee +++ b/frontend/ee @@ -1 +1 @@ -Subproject commit b8c4bdc50b7d38a10b96559823d01200d72669d0 +Subproject commit c5d9ed35fceeccf55bdd85751924ffc342332a10 diff --git a/server/ee b/server/ee index cc864000dd..8c0e6dec37 160000 --- a/server/ee +++ b/server/ee @@ -1 +1 @@ -Subproject commit cc864000dd03cc345e53ae9fc43821d3174f4c64 +Subproject commit 8c0e6dec37f1b0bb7fb5552d8eef4db3ddc18b31 From a931a38529cb098784b9652a1a9929084ba16b8f Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Thu, 10 Jul 2025 20:13:12 +0530 Subject: [PATCH 6/6] chore: updated submodule ref --- frontend/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ee b/frontend/ee index c5d9ed35fc..e95965cf14 160000 --- a/frontend/ee +++ b/frontend/ee @@ -1 +1 @@ -Subproject commit c5d9ed35fceeccf55bdd85751924ffc342332a10 +Subproject commit e95965cf14966d3f5727835adf5b8d3ca55a569e