Просмотр исходного кода

Memoize some editor React components (#28963)

GitOrigin-RevId: 6440df9ac3ef1bf1839dff07eb2f55b52b581d3e
Alf Eaton 10 месяцев назад
Родитель
Сommit
5b2e541186

+ 1 - 1
services/web/frontend/js/features/file-view/components/file-view-icons.tsx

@@ -1,7 +1,7 @@
 import MaterialIcon, { IconProps } from '@/shared/components/material-icon'
 
 export const LinkedFileIcon = (
-  props: Omit<IconProps, 'type' | 'modifier' | 'className' | 'unfilled'>
+  props: Omit<IconProps, 'type' | 'modifier' | 'className' | 'unfilled' | 'ref'>
 ) => {
   return (
     <MaterialIcon

+ 3 - 2
services/web/frontend/js/features/hotkeys-modal/components/hotkeys-modal.tsx

@@ -1,3 +1,4 @@
+import { memo } from 'react'
 import { Trans, useTranslation } from 'react-i18next'
 import HotkeysModalBottomText from './hotkeys-modal-bottom-text'
 import {
@@ -11,7 +12,7 @@ import OLButton from '@/shared/components/ol/ol-button'
 import OLRow from '@/shared/components/ol/ol-row'
 import OLCol from '@/shared/components/ol/ol-col'
 
-export default function HotkeysModal({
+export default memo(function HotkeysModal({
   animation = true,
   handleHide,
   show,
@@ -205,7 +206,7 @@ export default function HotkeysModal({
       </OLModalFooter>
     </OLModal>
   )
-}
+})
 
 function Hotkey({
   combination,

+ 3 - 3
services/web/frontend/js/features/ide-react/components/editor-survey.tsx

@@ -7,20 +7,20 @@ import { OLToast } from '@/shared/components/ol/ol-toast'
 import { OLToastContainer } from '@/shared/components/ol/ol-toast-container'
 import { useEditorContext } from '@/shared/context/editor-context'
 import useTutorial from '@/shared/hooks/promotions/use-tutorial'
-import { useCallback, useEffect, useMemo, useState } from 'react'
+import { memo, useCallback, useEffect, useMemo, useState } from 'react'
 import { sendMB } from '@/infrastructure/event-tracking'
 import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
 import { useTranslation } from 'react-i18next'
 
 type EditorSurveyPage = 'ease-of-use' | 'meets-my-needs' | 'thank-you'
 
-export default function EditorSurvey() {
+export default memo(function EditorSurvey() {
   return (
     <OLToastContainer className="editor-survey-toast">
       <EditorSurveyContent />
     </OLToastContainer>
   )
-}
+})
 
 const TUTORIAL_KEY = 'editor-popup-ux-survey'
 

+ 3 - 3
services/web/frontend/js/features/ide-react/components/global-toasts.tsx

@@ -1,6 +1,6 @@
 import { OLToast, OLToastProps } from '@/shared/components/ol/ol-toast'
 import useEventListener from '@/shared/hooks/use-event-listener'
-import { Fragment, ReactElement, useCallback, useState } from 'react'
+import { Fragment, memo, ReactElement, useCallback, useState } from 'react'
 
 import { debugConsole } from '@/utils/debugging'
 import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
@@ -30,7 +30,7 @@ const GENERATOR_MAP: Map<string, GlobalToastGenerator> = new Map(
 
 let toastCounter = 1
 
-export const GlobalToasts = () => {
+export const GlobalToasts = memo(function GlobalToasts() {
   const [toasts, setToasts] = useState<
     { component: ReactElement; id: string }[]
   >([])
@@ -96,4 +96,4 @@ export const GlobalToasts = () => {
       ))}
     </OLToastContainer>
   )
-}
+})

+ 146 - 143
services/web/frontend/js/features/review-panel/components/review-tooltip-menu.tsx

@@ -1,6 +1,7 @@
 import {
   CSSProperties,
   FC,
+  memo,
   useCallback,
   useEffect,
   useMemo,
@@ -94,166 +95,168 @@ const ReviewTooltipMenu: FC = () => {
   )
 }
 
-const ReviewTooltipMenuContent: FC<{ onAddComment: () => void }> = ({
-  onAddComment,
-}) => {
-  const { t } = useTranslation()
-  const view = useCodeMirrorViewContext()
-  const state = useCodeMirrorStateContext()
-  const { reviewPanelOpen } = useLayoutContext()
-  const ranges = useRangesContext()
-  const { acceptChanges, rejectChanges } = useRangesActionsContext()
-  const { showGenericConfirmModal } = useModalsContext()
-  const { wantTrackChanges } = useEditorPropertiesContext()
-  const [tooltipStyle, setTooltipStyle] = useState<CSSProperties | undefined>()
-  const [visible, setVisible] = useState(false)
+const ReviewTooltipMenuContent = memo<{ onAddComment: () => void }>(
+  function ReviewTooltipMenuContent({ onAddComment }) {
+    const { t } = useTranslation()
+    const view = useCodeMirrorViewContext()
+    const state = useCodeMirrorStateContext()
+    const { reviewPanelOpen } = useLayoutContext()
+    const ranges = useRangesContext()
+    const { acceptChanges, rejectChanges } = useRangesActionsContext()
+    const { showGenericConfirmModal } = useModalsContext()
+    const { wantTrackChanges } = useEditorPropertiesContext()
+    const [tooltipStyle, setTooltipStyle] = useState<
+      CSSProperties | undefined
+    >()
+    const [visible, setVisible] = useState(false)
 
-  const changesInSelection = useMemo(() => {
-    return (ranges?.changes ?? []).filter(({ op }) => {
-      const opFrom = op.p
-      const opLength = isInsertOperation(op) ? op.i.length : 0
-      const opTo = opFrom + opLength
-      const selection = state.selection.main
-      return opFrom >= selection.from && opTo <= selection.to
-    })
-  }, [ranges, state.selection.main])
+    const changesInSelection = useMemo(() => {
+      return (ranges?.changes ?? []).filter(({ op }) => {
+        const opFrom = op.p
+        const opLength = isInsertOperation(op) ? op.i.length : 0
+        const opTo = opFrom + opLength
+        const selection = state.selection.main
+        return opFrom >= selection.from && opTo <= selection.to
+      })
+    }, [ranges, state.selection.main])
 
-  const acceptChangesHandler = useCallback(() => {
-    const nChanges = numberOfChangesInSelection(
+    const acceptChangesHandler = useCallback(() => {
+      const nChanges = numberOfChangesInSelection(
+        ranges,
+        view.state.selection.main
+      )
+      showGenericConfirmModal({
+        message: t('confirm_accept_selected_changes', { count: nChanges }),
+        title: t('accept_selected_changes'),
+        onConfirm: async () => {
+          await acceptChanges(...changesInSelection)
+        },
+        primaryVariant: 'danger',
+      })
+    }, [
+      acceptChanges,
+      changesInSelection,
       ranges,
-      view.state.selection.main
-    )
-    showGenericConfirmModal({
-      message: t('confirm_accept_selected_changes', { count: nChanges }),
-      title: t('accept_selected_changes'),
-      onConfirm: async () => {
-        await acceptChanges(...changesInSelection)
-      },
-      primaryVariant: 'danger',
-    })
-  }, [
-    acceptChanges,
-    changesInSelection,
-    ranges,
-    showGenericConfirmModal,
-    view,
-    t,
-  ])
+      showGenericConfirmModal,
+      view,
+      t,
+    ])
 
-  const rejectChangesHandler = useCallback(() => {
-    const nChanges = numberOfChangesInSelection(
+    const rejectChangesHandler = useCallback(() => {
+      const nChanges = numberOfChangesInSelection(
+        ranges,
+        view.state.selection.main
+      )
+      showGenericConfirmModal({
+        message: t('confirm_reject_selected_changes', { count: nChanges }),
+        title: t('reject_selected_changes'),
+        onConfirm: async () => {
+          await rejectChanges(...changesInSelection)
+        },
+        primaryVariant: 'danger',
+      })
+    }, [
+      showGenericConfirmModal,
+      t,
       ranges,
-      view.state.selection.main
-    )
-    showGenericConfirmModal({
-      message: t('confirm_reject_selected_changes', { count: nChanges }),
-      title: t('reject_selected_changes'),
-      onConfirm: async () => {
-        await rejectChanges(...changesInSelection)
-      },
-      primaryVariant: 'danger',
-    })
-  }, [
-    showGenericConfirmModal,
-    t,
-    ranges,
-    view,
-    rejectChanges,
-    changesInSelection,
-  ])
+      view,
+      rejectChanges,
+      changesInSelection,
+    ])
 
-  const showChangesButtons = changesInSelection.length > 0
+    const showChangesButtons = changesInSelection.length > 0
 
-  useEffect(() => {
-    view.requestMeasure({
-      key: 'review-tooltip-outside-viewport',
-      read(view) {
-        const cursorCoords = view.coordsAtPos(view.state.selection.main.head)
+    useEffect(() => {
+      view.requestMeasure({
+        key: 'review-tooltip-outside-viewport',
+        read(view) {
+          const cursorCoords = view.coordsAtPos(view.state.selection.main.head)
 
-        if (!cursorCoords) {
-          return
-        }
+          if (!cursorCoords) {
+            return
+          }
 
-        const scrollDomRect = view.scrollDOM.getBoundingClientRect()
-        const contentDomRect = view.contentDOM.getBoundingClientRect()
-        const editorRightPos = contentDomRect.right - CM_LINE_RIGHT_PADDING
+          const scrollDomRect = view.scrollDOM.getBoundingClientRect()
+          const contentDomRect = view.contentDOM.getBoundingClientRect()
+          const editorRightPos = contentDomRect.right - CM_LINE_RIGHT_PADDING
 
-        if (
-          cursorCoords.top > scrollDomRect.top &&
-          cursorCoords.top < scrollDomRect.bottom
-        ) {
-          return
-        }
+          if (
+            cursorCoords.top > scrollDomRect.top &&
+            cursorCoords.top < scrollDomRect.bottom
+          ) {
+            return
+          }
 
-        return {
-          position: 'fixed' as const,
-          top: scrollDomRect.top + EDIT_MODE_SWITCH_WIDGET_HEIGHT,
-          right: window.innerWidth - editorRightPos,
-        }
-      },
-      write(res) {
-        setTooltipStyle(res)
-      },
-    })
-  }, [view, reviewPanelOpen, wantTrackChanges])
+          return {
+            position: 'fixed' as const,
+            top: scrollDomRect.top + EDIT_MODE_SWITCH_WIDGET_HEIGHT,
+            right: window.innerWidth - editorRightPos,
+          }
+        },
+        write(res) {
+          setTooltipStyle(res)
+        },
+      })
+    }, [view, reviewPanelOpen, wantTrackChanges])
 
-  useEffect(() => {
-    setVisible(false)
-    const timeout = setTimeout(() => {
-      setVisible(true)
-    }, TOOLTIP_SHOW_DELAY)
+    useEffect(() => {
+      setVisible(false)
+      const timeout = setTimeout(() => {
+        setVisible(true)
+      }, TOOLTIP_SHOW_DELAY)
 
-    return () => {
-      clearTimeout(timeout)
-    }
-  }, [])
+      return () => {
+        clearTimeout(timeout)
+      }
+    }, [])
 
-  return (
-    <div
-      className={classNames('review-tooltip-menu', {
-        'review-tooltip-menu-visible': visible,
-      })}
-      style={tooltipStyle}
-    >
-      <button
-        className="review-tooltip-menu-button review-tooltip-add-comment-button"
-        onClick={onAddComment}
+    return (
+      <div
+        className={classNames('review-tooltip-menu', {
+          'review-tooltip-menu-visible': visible,
+        })}
+        style={tooltipStyle}
       >
-        <MaterialIcon type="chat" />
-        {t('add_comment')}
-      </button>
-      {showChangesButtons && (
-        <>
-          <div className="review-tooltip-menu-divider" />
-          <OLTooltip
-            id="accept-all-changes"
-            description={t('accept_selected_changes')}
-          >
-            <button
-              className="review-tooltip-menu-button"
-              onClick={acceptChangesHandler}
-              aria-label={t('accept_selected_changes')}
+        <button
+          className="review-tooltip-menu-button review-tooltip-add-comment-button"
+          onClick={onAddComment}
+        >
+          <MaterialIcon type="chat" />
+          {t('add_comment')}
+        </button>
+        {showChangesButtons && (
+          <>
+            <div className="review-tooltip-menu-divider" />
+            <OLTooltip
+              id="accept-all-changes"
+              description={t('accept_selected_changes')}
             >
-              <MaterialIcon type="check" />
-            </button>
-          </OLTooltip>
+              <button
+                className="review-tooltip-menu-button"
+                onClick={acceptChangesHandler}
+                aria-label={t('accept_selected_changes')}
+              >
+                <MaterialIcon type="check" />
+              </button>
+            </OLTooltip>
 
-          <OLTooltip
-            id="reject-all-changes"
-            description={t('reject_selected_changes')}
-          >
-            <button
-              className="review-tooltip-menu-button"
-              onClick={rejectChangesHandler}
-              aria-label={t('reject_selected_changes')}
+            <OLTooltip
+              id="reject-all-changes"
+              description={t('reject_selected_changes')}
             >
-              <MaterialIcon type="clear" />
-            </button>
-          </OLTooltip>
-        </>
-      )}
-    </div>
-  )
-}
+              <button
+                className="review-tooltip-menu-button"
+                onClick={rejectChangesHandler}
+                aria-label={t('reject_selected_changes')}
+              >
+                <MaterialIcon type="clear" />
+              </button>
+            </OLTooltip>
+          </>
+        )}
+      </div>
+    )
+  }
+)
 
 export default ReviewTooltipMenu

+ 2 - 2
services/web/frontend/js/shared/components/material-icon.tsx

@@ -1,5 +1,5 @@
 import classNames from 'classnames'
-import React from 'react'
+import React, { memo } from 'react'
 import unfilledIconTypes from '../../../fonts/material-symbols/unfilled-symbols.mjs'
 
 export type AvailableUnfilledIcon = (typeof unfilledIconTypes)[number]
@@ -53,4 +53,4 @@ function MaterialIcon({
   )
 }
 
-export default MaterialIcon
+export default memo(MaterialIcon)