Merge pull request #13240 from ToolJet/perf/performance-imp

[fix]: Removed unwanted re-renders
This commit is contained in:
Johnson Cherian 2025-07-10 22:05:43 +05:30 committed by GitHub
commit 1d5fabae0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 101 additions and 125 deletions

@ -1 +1 @@
Subproject commit dbb130bfd859ab795557a36dc26936aa2252e248
Subproject commit e95965cf14966d3f5727835adf5b8d3ca55a569e

View file

@ -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}
/>
</Suspense>
{isUserInZeroToOneFlow ? (
<ArtifactPreview darkMode={darkMode} isUserInZeroToOneFlow={isUserInZeroToOneFlow} />
) : (
@ -74,7 +72,7 @@ export const Editor = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
<AppCanvas moduleId={moduleId} appId={appId} switchDarkMode={switchDarkMode} darkMode={darkMode} />
<QueryPanel darkMode={darkMode} />
<RightSidebarToggle darkMode={darkMode} />
{isRightSidebarOpen && <RightSideBar darkMode={darkMode} />}{' '}
<RightSideBar darkMode={darkMode} />
</DndProvider>
<Popups darkMode={darkMode} />
</>

View file

@ -16,7 +16,6 @@ import {
RIGHT_SIDEBAR_WIDTH,
} 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';
@ -25,18 +24,18 @@ 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';
import { debounce } from 'lodash';
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;
@ -46,18 +45,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);
@ -65,10 +60,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,
}),
@ -76,12 +70,10 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode })
);
const showHeader = !globalSettings?.hideHeader;
const { definition: { styles = {}, properties = {} } = {} } = pageSettings ?? {};
const { definition: { properties = {} } = {} } = pageSettings ?? {};
const { position, disableMenu, showOnDesktop } = properties ?? {};
const isPagesSidebarHidden = resolveReferences(disableMenu?.value);
const hideSidebar = isModuleMode || isPagesSidebarHidden || appType === 'module';
useEffect(() => {
// Need to remove this if we shift setExposedVariable Logic outside of components
// Currently present to run onLoadQueries after the component is mounted
@ -90,7 +82,7 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode })
}, []);
useEffect(() => {
function handleResize() {
function handleResizeImmediate() {
const _canvasWidth =
moduleId === 'canvas'
? document.getElementById('real-canvas')?.getBoundingClientRect()?.width
@ -98,6 +90,8 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode })
if (_canvasWidth !== 0) setCanvasWidth(_canvasWidth);
}
const handleResize = debounce(handleResizeImmediate, 300);
if (moduleId === 'canvas') {
window.addEventListener('resize', handleResize);
} else {
@ -108,15 +102,17 @@ export const AppCanvas = ({ appId, isViewer = false, 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]);
useEffect(() => {}, [isViewerSidebarPinned]);
const canvasContainerStyles = useMemo(() => {
const canvasBgColor =
currentMode === 'view'
@ -211,7 +207,6 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode })
<PagesSidebarNavigation
showHeader={showHeader}
isMobileDevice={currentLayout === 'mobile'}
pages={pages}
currentPageId={currentPageId ?? homePageId}
switchPage={switchPage}
height={currentMode === 'edit' ? canvasContainerHeight : '100%'}

View file

@ -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(
</div>
);
};
const sortedComponents = useSortedComponents(components, currentLayout, id);
const sortedComponents = useSortedComponents(components, currentLayout, id, moduleId);
return (
<div
@ -301,6 +301,7 @@ export const Container = React.memo(
mode={currentMode}
currentLayout={currentLayout}
darkMode={darkMode}
moduleId={moduleId}
/>
))}
</div>
@ -309,3 +310,7 @@ export const Container = React.memo(
);
}
);
Container.displayName = 'Container';
export { Container };

View file

@ -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 =

View file

@ -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;
@ -219,4 +217,7 @@ const RenderWidget = ({
</ErrorBoundary>
);
};
RenderWidget.displayName = 'RenderWidget';
export default memo(RenderWidget);

View file

@ -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}
/>
</div>
<ResizeGhostWidget isResizing={isResizing} />
@ -135,4 +135,6 @@ const WidgetWrapper = memo(
}
);
WidgetWrapper.displayName = 'WidgetWrapper';
export default WidgetWrapper;

View file

@ -37,8 +37,6 @@ export const DragLayer = ({ index, component, isModuleTab = false, disabled = fa
toggleRightSidebar(!isRightSidebarOpen);
}
setShowModuleBorder(true);
} else {
setShowModuleBorder(false);
}
}, [isDragging, setShowModuleBorder, isModuleEditor, toggleRightSidebar]);

View file

@ -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);

View file

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useState } from 'react';
import useStore from '@/AppBuilder/_stores/store';
import { ComponentConfigurationTab } from './ComponentConfigurationTab';
import ComponentsManagerTab from './ComponentManagerTab';
@ -13,13 +13,8 @@ export const RightSideBar = ({ darkMode }) => {
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 [popoverContentHeight, setPopoverContentHeight] = useState(queryPanelHeight);
const sidebarRef = useRef(null);
useEffect(() => {
if (!isDraggingQueryPane) {
setPopoverContentHeight(
@ -31,30 +26,10 @@ export const RightSideBar = ({ darkMode }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [queryPanelHeight, isDraggingQueryPane]);
// 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 (
<div ref={sidebarRef} className="sub-section">
<div className="sub-section">
<div
style={{ height: `${popoverContentHeight}vh`, overflow: 'auto' }}
className={cx('editor-sidebar', { 'dark-theme theme-dark': darkMode })}

View file

@ -18,7 +18,6 @@ const MobileHeader = ({
appName,
changeToDarkMode,
darkMode,
pages,
currentPageId,
switchPage,
setAppDefinitionFromVersion,
@ -61,7 +60,6 @@ const MobileHeader = ({
const _renderMobileNavigationMenu = () => (
<MobileNavigationMenu
pages={pages}
currentPageId={currentPageId}
switchPage={switchPage}
darkMode={darkMode}

View file

@ -15,11 +15,12 @@ import { Link } from 'react-router-dom';
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
import OverflowTooltip from '@/_components/OverflowTooltip';
const RenderGroup = ({ pages, pageGroup, currentPage, darkMode, handlepageSwitch, currentPageId, icon }) => {
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);
};

View file

@ -17,7 +17,6 @@ import { ModuleProvider } from '@/AppBuilder/_contexts/ModuleContext';
import { getPatToken, setPatToken } from '@/AppBuilder/EmbedApp';
import Spinner from '@/_ui/Spinner';
import { checkIfLicenseNotValid } from '@/_helpers/appUtils';
import toast from 'react-hot-toast';
import TooljetBanner from '../../Editor/Viewer/TooljetBanner';
export const Viewer = ({
@ -43,7 +42,6 @@ export const Viewer = ({
currentCanvasWidth,
currentPageId,
globalSettings,
pages,
pageSettings,
updateCanvasHeight,
appName,
@ -64,9 +62,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,
@ -79,7 +74,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;
@ -106,7 +100,7 @@ export const Viewer = ({
}, [isSidebarPinned]);
const { definition: { properties = {} } = {} } = pageSettings ?? {};
const { position, hideHeader } = properties ?? {};
const { position } = properties ?? {};
const canvasRef = useRef(null);
const isMobilePreviewMode = selectedVersion?.id && currentLayout === 'mobile';
@ -160,7 +154,6 @@ export const Viewer = ({
isAppLoaded={isAppLoaded}
appName={appName}
darkMode={darkMode}
pages={pages}
currentPageId={currentPageId ?? homePageId}
showViewerNavigation={!hideSidebar}
handleAppEnvironmentChanged={handleAppEnvironmentChanged}
@ -176,7 +169,6 @@ export const Viewer = ({
showHeader={showHeader}
appName={appName}
darkMode={darkMode}
pages={pages}
currentPageId={currentPageId ?? homePageId}
showViewerNavigation={!hideSidebar}
handleAppEnvironmentChanged={handleAppEnvironmentChanged}
@ -260,7 +252,6 @@ export const Viewer = ({
showHeader={showHeader && isAppLoaded}
appName={appName}
darkMode={darkMode}
pages={pages}
currentPageId={currentPageId ?? homePageId}
showViewerNavigation={!hideSidebar}
handleAppEnvironmentChanged={handleAppEnvironmentChanged}

View file

@ -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;
@ -542,10 +535,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, {});
});
}
@ -555,12 +545,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;
@ -655,7 +645,7 @@ const useAppData = (
}
});
// fetchDataSources(currentVersionId, selectedEnvironment.id);
fetchGlobalDataSources(app.organizationId, currentVersionId, selectedEnvironment.id);
fetchGlobalDataSources(organizationId, currentVersionId, selectedEnvironment.id);
setResolvedConstants(orgConstants);
setSecrets(orgSecrets);
}

View file

@ -2,23 +2,43 @@ 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
// 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;
if (isForcedUpdate) {
prevForceUpdateRef.current = triggerUpdate;
if (containerId !== id) {
return prevComponentsOrder.current;
}
}
// 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();
@ -41,7 +61,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;
};

View file

@ -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'),
});

View file

@ -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]);