Memoize FileTree and outline toggle button components (#16776)

GitOrigin-RevId: 299ed9d568650ce37edba87643112d1cd6d12fd4
This commit is contained in:
Alf Eaton 2024-02-01 09:41:12 +00:00 committed by Copybot
parent f06f2ef99e
commit c443322a41
8 changed files with 93 additions and 58 deletions

View file

@ -176,7 +176,10 @@ export const ChatContext = createContext<
>(undefined) >(undefined)
export const ChatProvider: FC = ({ children }) => { export const ChatProvider: FC = ({ children }) => {
const clientId = useRef(uuid()) const clientId = useRef<string>()
if (clientId.current === undefined) {
clientId.current = uuid()
}
const user = useUserContext() const user = useUserContext()
const { _id: projectId } = useProjectContext() const { _id: projectId } = useProjectContext()

View file

@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react' import React, { memo, useCallback, useState } from 'react'
import { useUserContext } from '@/shared/context/user-context' import { useUserContext } from '@/shared/context/user-context'
import { useReferencesContext } from '@/features/ide-react/context/references-context' import { useReferencesContext } from '@/features/ide-react/context/references-context'
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
@ -7,7 +7,7 @@ import { RefProviders } from '../../../../../types/user'
import FileTreeRoot from '@/features/file-tree/components/file-tree-root' import FileTreeRoot from '@/features/file-tree/components/file-tree-root'
import { useFileTreeOpenContext } from '@/features/ide-react/context/file-tree-open-context' import { useFileTreeOpenContext } from '@/features/ide-react/context/file-tree-open-context'
export function FileTree() { export const FileTree = memo(function FileTree() {
const user = useUserContext() const user = useUserContext()
const { indexAllReferences } = useReferencesContext() const { indexAllReferences } = useReferencesContext()
const { setStartedFreeTrial } = useIdeReactContext() const { setStartedFreeTrial } = useIdeReactContext()
@ -44,4 +44,4 @@ export function FileTree() {
/> />
</div> </div>
) )
} })

View file

@ -0,0 +1,24 @@
import { memo, type Dispatch, type SetStateAction } from 'react'
import Icon from '@/shared/components/icon'
import { useTranslation } from 'react-i18next'
export const OutlineItemToggleButton = memo<{
expanded: boolean
setExpanded: Dispatch<SetStateAction<boolean>>
}>(({ expanded, setExpanded }) => {
const { t } = useTranslation()
return (
<button
className="outline-item-expand-collapse-btn"
onClick={() => setExpanded(value => !value)}
aria-label={expanded ? t('collapse') : t('expand')}
>
<Icon
type={expanded ? 'angle-down' : 'angle-right'}
className="outline-caret-icon"
/>
</button>
)
})
OutlineItemToggleButton.displayName = 'OutlineItemToggleButton'

View file

@ -2,9 +2,8 @@ import { useState, useEffect, useRef, memo } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed' import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
import classNames from 'classnames' import classNames from 'classnames'
import { useTranslation } from 'react-i18next'
import OutlineList from './outline-list' import OutlineList from './outline-list'
import Icon from '../../../shared/components/icon' import { OutlineItemToggleButton } from '@/features/outline/components/outline-item-toggle-button'
const OutlineItem = memo(function OutlineItem({ const OutlineItem = memo(function OutlineItem({
outlineItem, outlineItem,
@ -13,8 +12,6 @@ const OutlineItem = memo(function OutlineItem({
matchesHighlightedLine, matchesHighlightedLine,
containsHighlightedLine, containsHighlightedLine,
}) { }) {
const { t } = useTranslation()
const [expanded, setExpanded] = useState(true) const [expanded, setExpanded] = useState(true)
const titleElementRef = useRef() const titleElementRef = useRef()
const isHighlightedRef = useRef(false) const isHighlightedRef = useRef(false)
@ -30,10 +27,6 @@ const OutlineItem = memo(function OutlineItem({
'outline-item-link-highlight': isHighlighted, 'outline-item-link-highlight': isHighlighted,
}) })
function handleExpandCollapseClick() {
setExpanded(!expanded)
}
function handleOutlineItemLinkClick(event) { function handleOutlineItemLinkClick(event) {
const syncToPdf = event.detail === 2 // double-click = sync to PDF const syncToPdf = event.detail === 2 // double-click = sync to PDF
jumpToLine(outlineItem.line, syncToPdf) jumpToLine(outlineItem.line, syncToPdf)
@ -65,18 +58,12 @@ const OutlineItem = memo(function OutlineItem({
aria-label={outlineItem.title} aria-label={outlineItem.title}
> >
<div className="outline-item-row"> <div className="outline-item-row">
{outlineItem.children ? ( {!!outlineItem.children && (
<button <OutlineItemToggleButton
className="outline-item-expand-collapse-btn" expanded={expanded}
onClick={handleExpandCollapseClick} setExpanded={setExpanded}
aria-label={expanded ? t('collapse') : t('expand')}
>
<Icon
type={expanded ? 'angle-down' : 'angle-right'}
className="outline-caret-icon"
/> />
</button> )}
) : null}
<button <button
className={itemLinkClasses} className={itemLinkClasses}
onClick={handleOutlineItemLinkClick} onClick={handleOutlineItemLinkClick}

View file

@ -1,11 +1,9 @@
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import classNames from 'classnames' import classNames from 'classnames'
import { useTranslation } from 'react-i18next'
import OutlineRoot from './outline-root' import OutlineRoot from './outline-root'
import Icon from '../../../shared/components/icon'
import withErrorBoundary from '../../../infrastructure/error-boundary' import withErrorBoundary from '../../../infrastructure/error-boundary'
import Tooltip from '../../../shared/components/tooltip' import { OutlineToggleButton } from '@/features/outline/components/outline-toggle-button'
const OutlinePane = React.memo<{ const OutlinePane = React.memo<{
isTexFile: boolean isTexFile: boolean
@ -28,8 +26,6 @@ const OutlinePane = React.memo<{
expanded, expanded,
toggleExpanded, toggleExpanded,
}) { }) {
const { t } = useTranslation()
const isOpen = Boolean(isTexFile && expanded) const isOpen = Boolean(isTexFile && expanded)
useEffect(() => { useEffect(() => {
@ -43,32 +39,13 @@ const OutlinePane = React.memo<{
return ( return (
<div className={headerClasses}> <div className={headerClasses}>
<header className="outline-header"> <header className="outline-header">
<button <OutlineToggleButton
className="outline-header-expand-collapse-btn" toggleExpanded={toggleExpanded}
disabled={!isTexFile} expanded={expanded}
onClick={toggleExpanded} isOpen={isOpen}
aria-label={expanded ? t('hide_outline') : t('show_outline')} isPartial={isPartial}
> isTexFile={isTexFile}
<Icon
type={isOpen ? 'angle-down' : 'angle-right'}
className="outline-caret-icon"
/> />
<h4 className="outline-header-name">{t('file_outline')}</h4>
{isPartial && (
<Tooltip
id="partial-outline"
description={t('partial_outline_warning')}
overlayProps={{ placement: 'top' }}
>
<span role="status">
<Icon
type="exclamation-triangle"
aria-label={t('partial_outline_warning')}
/>
</span>
</Tooltip>
)}
</button>
</header> </header>
{isOpen && ( {isOpen && (
<div className="outline-body"> <div className="outline-body">

View file

@ -0,0 +1,44 @@
import Icon from '@/shared/components/icon'
import Tooltip from '@/shared/components/tooltip'
import React, { memo } from 'react'
import { useTranslation } from 'react-i18next'
export const OutlineToggleButton = memo<{
isTexFile: boolean
toggleExpanded: () => void
expanded?: boolean
isOpen: boolean
isPartial: boolean
}>(({ isTexFile, toggleExpanded, expanded, isOpen, isPartial }) => {
const { t } = useTranslation()
return (
<button
className="outline-header-expand-collapse-btn"
disabled={!isTexFile}
onClick={toggleExpanded}
aria-label={expanded ? t('hide_outline') : t('show_outline')}
>
<Icon
type={isOpen ? 'angle-down' : 'angle-right'}
className="outline-caret-icon"
/>
<h4 className="outline-header-name">{t('file_outline')}</h4>
{isPartial && (
<Tooltip
id="partial-outline"
description={t('partial_outline_warning')}
overlayProps={{ placement: 'top' }}
>
<span role="status">
<Icon
type="exclamation-triangle"
aria-label={t('partial_outline_warning')}
/>
</span>
</Tooltip>
)}
</button>
)
})
OutlineToggleButton.displayName = 'OutlineToggleButton'

View file

@ -1,11 +1,11 @@
import { createContext, FC, useContext } from 'react' import { createContext, FC, useContext, useMemo } from 'react'
import getMeta from '../../utils/meta' import getMeta from '../../utils/meta'
import { User } from '../../../../types/user' import { User } from '../../../../types/user'
export const UserContext = createContext<User | undefined>(undefined) export const UserContext = createContext<User | undefined>(undefined)
export const UserProvider: FC = ({ children }) => { export const UserProvider: FC = ({ children }) => {
const user = getMeta('ol-user') const user = useMemo(() => getMeta('ol-user'), [])
return <UserContext.Provider value={user}>{children}</UserContext.Provider> return <UserContext.Provider value={user}>{children}</UserContext.Provider>
} }

View file

@ -43,7 +43,7 @@ function setTitle(title: string) {
} }
function useBrowserWindow() { function useBrowserWindow() {
const [hasFocus, setHasFocus] = useState(document.hasFocus()) const [hasFocus, setHasFocus] = useState(() => document.hasFocus())
useEffect(() => { useEffect(() => {
function handleFocusEvent() { function handleFocusEvent() {