Explorar o código

optimize modals

Yuki Takei hai 7 meses
pai
achega
db7caaef8b

+ 22 - 6
apps/app/src/client/components/PageAccessoriesModal/PageAccessoriesModal.tsx

@@ -1,4 +1,6 @@
-import React, { useMemo, useState, type JSX } from 'react';
+import React, {
+  useMemo, useState, useCallback, type JSX,
+} from 'react';
 
 import { useAtomValue } from 'jotai';
 import { useTranslation } from 'next-i18next';
@@ -25,8 +27,7 @@ const PageAttachment = dynamic(() => import('./PageAttachment'), { ssr: false })
 const PageHistory = dynamic(() => import('./PageHistory').then(mod => mod.PageHistory), { ssr: false });
 const ShareLink = dynamic(() => import('./ShareLink').then(mod => mod.ShareLink), { ssr: false });
 
-
-export const PageAccessoriesModal = (): JSX.Element => {
+const PageAccessoriesModalSubstance = (): JSX.Element => {
 
   const { t } = useTranslation();
 
@@ -43,6 +44,7 @@ export const PageAccessoriesModal = (): JSX.Element => {
 
   useAutoOpenModalByQueryParam();
 
+  // Memoize heavy navTabMapping calculation
   const navTabMapping = useMemo(() => {
     return {
       [PageAccessoriesModalContents.PageHistory]: {
@@ -71,16 +73,20 @@ export const PageAccessoriesModal = (): JSX.Element => {
     };
   }, [t, close, isGuestUser, isReadOnlyUser, isSharedUser, isLinkSharingDisabled]);
 
+  // Memoize expand/contract handlers
+  const expandWindow = useCallback(() => setIsWindowExpanded(true), []);
+  const contractWindow = useCallback(() => setIsWindowExpanded(false), []);
+
   const buttons = useMemo(() => (
     <span className="me-3">
       <ExpandOrContractButton
         isWindowExpanded={isWindowExpanded}
-        expandWindow={() => setIsWindowExpanded(true)}
-        contractWindow={() => setIsWindowExpanded(false)}
+        expandWindow={expandWindow}
+        contractWindow={contractWindow}
       />
       <button type="button" className="btn btn-close ms-2" onClick={close} aria-label="Close"></button>
     </span>
-  ), [close, isWindowExpanded]);
+  ), [close, isWindowExpanded, expandWindow, contractWindow]);
 
   if (status == null || status.activatedContents == null) {
     return <></>;
@@ -124,3 +130,13 @@ export const PageAccessoriesModal = (): JSX.Element => {
     </Modal>
   );
 };
+
+export const PageAccessoriesModal = (): JSX.Element => {
+  const status = usePageAccessoriesModalStatus();
+
+  if (status == null || !status.isOpened) {
+    return <></>;
+  }
+
+  return <PageAccessoriesModalSubstance />;
+};

+ 33 - 13
apps/app/src/client/components/PageEditor/HandsontableModal.tsx

@@ -1,4 +1,6 @@
-import React, { useState, type JSX } from 'react';
+import React, {
+  useState, useCallback, useMemo, type JSX,
+} from 'react';
 
 import { MarkdownTable } from '@growi/editor';
 import { useHandsontableModalForEditor } from '@growi/editor/dist/client/stores/use-handsontable';
@@ -30,7 +32,7 @@ const MARKDOWNTABLE_TO_HANDSONTABLE_ALIGNMENT_SYMBOL_MAPPING = {
   '': '',
 };
 
-export const HandsontableModal = (): JSX.Element => {
+export const HandsontableModalSubstance = (): JSX.Element => {
 
   const { t } = useTranslation('commons');
   const handsontableModalData = useHandsontableModalStatus();
@@ -44,7 +46,8 @@ export const HandsontableModal = (): JSX.Element => {
   const editor = handsontableModalForEditorData?.editor;
   const onSave = handsontableModalData?.onSave;
 
-  const defaultMarkdownTable = () => {
+  // Memoize default table creation
+  const defaultMarkdownTable = useMemo(() => {
     return new MarkdownTable(
       [
         ['col1', 'col2', 'col3'],
@@ -55,7 +58,7 @@ export const HandsontableModal = (): JSX.Element => {
         align: ['', '', ''],
       },
     );
-  };
+  }, []);
 
   const defaultHandsontableSetting = () => {
     return {
@@ -95,36 +98,42 @@ export const HandsontableModal = (): JSX.Element => {
   const [handsontableHeight, setHandsontableHeight] = useState<number>(DEFAULT_HOT_HEIGHT);
   const [handsontableWidth, setHandsontableWidth] = useState<number>(0);
 
-  const handleWindowExpandedChange = () => {
+  // Memoize window resize handler
+  const handleWindowExpandedChange = useCallback(() => {
     if (hotTableContainer != null) {
       // Get the width and height of hotTableContainer
       const { width, height } = hotTableContainer.getBoundingClientRect();
       setHandsontableWidth(width);
       setHandsontableHeight(height);
     }
-  };
+  }, [hotTableContainer]);
 
-  const debouncedHandleWindowExpandedChange = debounce(100, handleWindowExpandedChange);
+  // Memoize debounced handler
+  const debouncedHandleWindowExpandedChange = useMemo(() => (
+    debounce(100, handleWindowExpandedChange)
+  ), [handleWindowExpandedChange]);
 
-  const handleModalOpen = () => {
+  // Memoize modal open handler
+  const handleModalOpen = useCallback(() => {
     const markdownTableState = table == null && editor != null ? getMarkdownTable(editor) : table;
     const initTableInstance = markdownTableState == null ? defaultMarkdownTable : markdownTableState.clone();
     setMarkdownTable(markdownTableState ?? defaultMarkdownTable);
     setMarkdownTableOnInit(initTableInstance);
     debouncedHandleWindowExpandedChange();
-  };
+  }, [table, editor, defaultMarkdownTable, debouncedHandleWindowExpandedChange]);
 
-  const expandWindow = () => {
+  // Memoize expand/contract handlers
+  const expandWindow = useCallback(() => {
     setIsWindowExpanded(true);
     debouncedHandleWindowExpandedChange();
-  };
+  }, [debouncedHandleWindowExpandedChange]);
 
-  const contractWindow = () => {
+  const contractWindow = useCallback(() => {
     setIsWindowExpanded(false);
     // Set the height to the default value
     setHandsontableHeight(DEFAULT_HOT_HEIGHT);
     debouncedHandleWindowExpandedChange();
-  };
+  }, [debouncedHandleWindowExpandedChange]);
 
   const markdownTableOption = {
     get latest() {
@@ -525,3 +534,14 @@ export const HandsontableModal = (): JSX.Element => {
     </Modal>
   );
 };
+
+export const HandsontableModal = (): JSX.Element => {
+  const handsontableModalData = useHandsontableModalStatus();
+  const isOpened = handsontableModalData?.isOpened ?? false;
+
+  if (!isOpened) {
+    return <></>;
+  }
+
+  return <HandsontableModalSubstance />;
+};