mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-07 14:32:16 +00:00
Merge pull request #4188 from overleaf/ae-memo
Improve React performance by memoizing components and values GitOrigin-RevId: 805278b8b7ac04c3dc4b078fa53cc0e3770d261b
This commit is contained in:
parent
d64172cfff
commit
eebeffc1c5
11 changed files with 216 additions and 157 deletions
|
@ -13,7 +13,7 @@ import withErrorBoundary from '../../../infrastructure/error-boundary'
|
|||
import { FetchError } from '../../../infrastructure/fetch-json'
|
||||
import { useChatContext } from '../context/chat-context'
|
||||
|
||||
function ChatPane() {
|
||||
const ChatPane = React.memo(function ChatPane() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { chatIsOpen } = useLayoutContext({ chatIsOpen: PropTypes.bool })
|
||||
|
@ -81,7 +81,7 @@ function ChatPane() {
|
|||
/>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function LoadingSpinner() {
|
||||
const { t } = useTranslation()
|
||||
|
|
|
@ -264,19 +264,34 @@ export function ChatProvider({ children }) {
|
|||
markMessagesAsRead,
|
||||
])
|
||||
|
||||
const value = {
|
||||
status: state.status,
|
||||
messages: state.messages,
|
||||
initialMessagesLoaded: state.initialMessagesLoaded,
|
||||
atEnd: state.atEnd,
|
||||
unreadMessageCount: state.unreadMessageCount,
|
||||
loadInitialMessages,
|
||||
loadMoreMessages,
|
||||
reset,
|
||||
sendMessage,
|
||||
markMessagesAsRead,
|
||||
error: state.error,
|
||||
}
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
status: state.status,
|
||||
messages: state.messages,
|
||||
initialMessagesLoaded: state.initialMessagesLoaded,
|
||||
atEnd: state.atEnd,
|
||||
unreadMessageCount: state.unreadMessageCount,
|
||||
loadInitialMessages,
|
||||
loadMoreMessages,
|
||||
reset,
|
||||
sendMessage,
|
||||
markMessagesAsRead,
|
||||
error: state.error,
|
||||
}),
|
||||
[
|
||||
loadInitialMessages,
|
||||
loadMoreMessages,
|
||||
markMessagesAsRead,
|
||||
reset,
|
||||
sendMessage,
|
||||
state.atEnd,
|
||||
state.error,
|
||||
state.initialMessagesLoaded,
|
||||
state.messages,
|
||||
state.status,
|
||||
state.unreadMessageCount,
|
||||
]
|
||||
)
|
||||
|
||||
return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>
|
||||
}
|
||||
|
|
|
@ -35,99 +35,104 @@ const chatContextPropTypes = {
|
|||
unreadMessageCount: PropTypes.number.isRequired,
|
||||
}
|
||||
|
||||
function EditorNavigationToolbarRoot({
|
||||
onlineUsersArray,
|
||||
openDoc,
|
||||
openShareProjectModal,
|
||||
}) {
|
||||
const { user } = useApplicationContext(applicationContextPropTypes)
|
||||
const EditorNavigationToolbarRoot = React.memo(
|
||||
function EditorNavigationToolbarRoot({
|
||||
onlineUsersArray,
|
||||
openDoc,
|
||||
openShareProjectModal,
|
||||
}) {
|
||||
const { user } = useApplicationContext(applicationContextPropTypes)
|
||||
|
||||
const {
|
||||
cobranding,
|
||||
loading,
|
||||
isRestrictedTokenMember,
|
||||
projectName,
|
||||
renameProject,
|
||||
isProjectOwner,
|
||||
} = useEditorContext(editorContextPropTypes)
|
||||
const {
|
||||
cobranding,
|
||||
loading,
|
||||
isRestrictedTokenMember,
|
||||
projectName,
|
||||
renameProject,
|
||||
isProjectOwner,
|
||||
} = useEditorContext(editorContextPropTypes)
|
||||
|
||||
const {
|
||||
chatIsOpen,
|
||||
setChatIsOpen,
|
||||
reviewPanelOpen,
|
||||
setReviewPanelOpen,
|
||||
view,
|
||||
setView,
|
||||
setLeftMenuShown,
|
||||
pdfLayout,
|
||||
} = useLayoutContext(layoutContextPropTypes)
|
||||
const {
|
||||
chatIsOpen,
|
||||
setChatIsOpen,
|
||||
reviewPanelOpen,
|
||||
setReviewPanelOpen,
|
||||
view,
|
||||
setView,
|
||||
setLeftMenuShown,
|
||||
pdfLayout,
|
||||
} = useLayoutContext(layoutContextPropTypes)
|
||||
|
||||
const { markMessagesAsRead, unreadMessageCount } = useChatContext(
|
||||
chatContextPropTypes
|
||||
)
|
||||
const { markMessagesAsRead, unreadMessageCount } = useChatContext(
|
||||
chatContextPropTypes
|
||||
)
|
||||
|
||||
const toggleChatOpen = useCallback(() => {
|
||||
if (!chatIsOpen) {
|
||||
markMessagesAsRead()
|
||||
}
|
||||
setChatIsOpen(value => !value)
|
||||
}, [chatIsOpen, setChatIsOpen, markMessagesAsRead])
|
||||
const toggleChatOpen = useCallback(() => {
|
||||
if (!chatIsOpen) {
|
||||
markMessagesAsRead()
|
||||
}
|
||||
setChatIsOpen(value => !value)
|
||||
}, [chatIsOpen, setChatIsOpen, markMessagesAsRead])
|
||||
|
||||
const toggleReviewPanelOpen = useCallback(
|
||||
() => setReviewPanelOpen(value => !value),
|
||||
[setReviewPanelOpen]
|
||||
)
|
||||
const toggleReviewPanelOpen = useCallback(
|
||||
() => setReviewPanelOpen(value => !value),
|
||||
[setReviewPanelOpen]
|
||||
)
|
||||
|
||||
const toggleHistoryOpen = useCallback(() => {
|
||||
setView(view === 'history' ? 'editor' : 'history')
|
||||
}, [view, setView])
|
||||
const toggleHistoryOpen = useCallback(() => {
|
||||
setView(view === 'history' ? 'editor' : 'history')
|
||||
}, [view, setView])
|
||||
|
||||
const togglePdfView = useCallback(() => {
|
||||
setView(view === 'pdf' ? 'editor' : 'pdf')
|
||||
}, [view, setView])
|
||||
const togglePdfView = useCallback(() => {
|
||||
setView(view === 'pdf' ? 'editor' : 'pdf')
|
||||
}, [view, setView])
|
||||
|
||||
const openShareModal = useCallback(() => {
|
||||
openShareProjectModal(isProjectOwner)
|
||||
}, [openShareProjectModal, isProjectOwner])
|
||||
const openShareModal = useCallback(() => {
|
||||
openShareProjectModal(isProjectOwner)
|
||||
}, [openShareProjectModal, isProjectOwner])
|
||||
|
||||
const onShowLeftMenuClick = useCallback(
|
||||
() => setLeftMenuShown(value => !value),
|
||||
[setLeftMenuShown]
|
||||
)
|
||||
const onShowLeftMenuClick = useCallback(
|
||||
() => setLeftMenuShown(value => !value),
|
||||
[setLeftMenuShown]
|
||||
)
|
||||
|
||||
function goToUser(user) {
|
||||
if (user.doc && typeof user.row === 'number') {
|
||||
openDoc(user.doc, { gotoLine: user.row + 1 })
|
||||
}
|
||||
const goToUser = useCallback(
|
||||
user => {
|
||||
if (user.doc && typeof user.row === 'number') {
|
||||
openDoc(user.doc, { gotoLine: user.row + 1 })
|
||||
}
|
||||
},
|
||||
[openDoc]
|
||||
)
|
||||
|
||||
// using {display: 'none'} as 1:1 migration from Angular's ng-hide. Using
|
||||
// `loading ? null : <ToolbarHeader/>` causes UI glitches
|
||||
return (
|
||||
<ToolbarHeader
|
||||
style={loading ? { display: 'none' } : {}}
|
||||
cobranding={cobranding}
|
||||
onShowLeftMenuClick={onShowLeftMenuClick}
|
||||
chatIsOpen={chatIsOpen}
|
||||
unreadMessageCount={unreadMessageCount}
|
||||
toggleChatOpen={toggleChatOpen}
|
||||
reviewPanelOpen={reviewPanelOpen}
|
||||
toggleReviewPanelOpen={toggleReviewPanelOpen}
|
||||
historyIsOpen={view === 'history'}
|
||||
toggleHistoryOpen={toggleHistoryOpen}
|
||||
onlineUsers={onlineUsersArray}
|
||||
goToUser={goToUser}
|
||||
isRestrictedTokenMember={isRestrictedTokenMember}
|
||||
isAnonymousUser={user == null}
|
||||
projectName={projectName}
|
||||
renameProject={renameProject}
|
||||
openShareModal={openShareModal}
|
||||
pdfViewIsOpen={view === 'pdf'}
|
||||
pdfButtonIsVisible={pdfLayout === 'flat'}
|
||||
togglePdfView={togglePdfView}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// using {display: 'none'} as 1:1 migration from Angular's ng-hide. Using
|
||||
// `loading ? null : <ToolbarHeader/>` causes UI glitches
|
||||
return (
|
||||
<ToolbarHeader
|
||||
style={loading ? { display: 'none' } : {}}
|
||||
cobranding={cobranding}
|
||||
onShowLeftMenuClick={onShowLeftMenuClick}
|
||||
chatIsOpen={chatIsOpen}
|
||||
unreadMessageCount={unreadMessageCount}
|
||||
toggleChatOpen={toggleChatOpen}
|
||||
reviewPanelOpen={reviewPanelOpen}
|
||||
toggleReviewPanelOpen={toggleReviewPanelOpen}
|
||||
historyIsOpen={view === 'history'}
|
||||
toggleHistoryOpen={toggleHistoryOpen}
|
||||
onlineUsers={onlineUsersArray}
|
||||
goToUser={goToUser}
|
||||
isRestrictedTokenMember={isRestrictedTokenMember}
|
||||
isAnonymousUser={user == null}
|
||||
projectName={projectName}
|
||||
renameProject={renameProject}
|
||||
openShareModal={openShareModal}
|
||||
pdfViewIsOpen={view === 'pdf'}
|
||||
pdfButtonIsVisible={pdfLayout === 'flat'}
|
||||
togglePdfView={togglePdfView}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
EditorNavigationToolbarRoot.propTypes = {
|
||||
onlineUsersArray: PropTypes.array.isRequired,
|
||||
|
|
|
@ -15,7 +15,7 @@ import importOverleafModules from '../../../../macros/import-overleaf-module.mac
|
|||
const [publishModalModules] = importOverleafModules('publishModal')
|
||||
const PublishButton = publishModalModules?.import.default
|
||||
|
||||
function ToolbarHeader({
|
||||
const ToolbarHeader = React.memo(function ToolbarHeader({
|
||||
cobranding,
|
||||
onShowLeftMenuClick,
|
||||
chatIsOpen,
|
||||
|
@ -90,7 +90,7 @@ function ToolbarHeader({
|
|||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
ToolbarHeader.propTypes = {
|
||||
onShowLeftMenuClick: PropTypes.func.isRequired,
|
||||
|
|
|
@ -18,7 +18,7 @@ import { useDroppable } from '../contexts/file-tree-draggable'
|
|||
import { useFileTreeSocketListener } from '../hooks/file-tree-socket-listener'
|
||||
import FileTreeModalCreateFile from './modals/file-tree-modal-create-file'
|
||||
|
||||
function FileTreeRoot({
|
||||
const FileTreeRoot = React.memo(function FileTreeRoot({
|
||||
projectId,
|
||||
rootFolder,
|
||||
rootDocId,
|
||||
|
@ -64,7 +64,7 @@ function FileTreeRoot({
|
|||
<FileTreeModalError />
|
||||
</FileTreeContext>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function FileTreeRootFolder() {
|
||||
useFileTreeSocketListener()
|
||||
|
|
|
@ -9,7 +9,7 @@ import localStorage from '../../../infrastructure/local-storage'
|
|||
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||
|
||||
function OutlinePane({
|
||||
const OutlinePane = React.memo(function OutlinePane({
|
||||
isTexFile,
|
||||
outline,
|
||||
jumpToLine,
|
||||
|
@ -73,7 +73,7 @@ function OutlinePane({
|
|||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
OutlinePane.propTypes = {
|
||||
isTexFile: PropTypes.bool.isRequired,
|
||||
|
|
|
@ -5,7 +5,7 @@ import PreviewLogsPane from './preview-logs-pane'
|
|||
import PreviewFirstErrorPopUp from './preview-first-error-pop-up'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
function PreviewPane({
|
||||
const PreviewPane = React.memo(function PreviewPane({
|
||||
compilerState,
|
||||
onClearCache,
|
||||
onRecompile,
|
||||
|
@ -139,7 +139,7 @@ function PreviewPane({
|
|||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
PreviewPane.propTypes = {
|
||||
compilerState: PropTypes.shape({
|
||||
|
|
|
@ -79,7 +79,7 @@ export function useProjectContext() {
|
|||
return context
|
||||
}
|
||||
|
||||
export default function ShareProjectModal({
|
||||
const ShareProjectModal = React.memo(function ShareProjectModal({
|
||||
handleHide,
|
||||
show,
|
||||
animation = true,
|
||||
|
@ -161,10 +161,12 @@ export default function ShareProjectModal({
|
|||
</ProjectContext.Provider>
|
||||
</ShareProjectContext.Provider>
|
||||
)
|
||||
}
|
||||
})
|
||||
ShareProjectModal.propTypes = {
|
||||
animation: PropTypes.bool,
|
||||
handleHide: PropTypes.func.isRequired,
|
||||
isAdmin: PropTypes.bool.isRequired,
|
||||
show: PropTypes.bool.isRequired,
|
||||
}
|
||||
|
||||
export default ShareProjectModal
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { createContext, useContext } from 'react'
|
||||
import React, { createContext, useContext, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export const ApplicationContext = createContext()
|
||||
|
@ -15,16 +15,20 @@ ApplicationContext.Provider.propTypes = {
|
|||
}
|
||||
|
||||
export function ApplicationProvider({ children }) {
|
||||
const applicationContextValue = {
|
||||
gitBridgePublicBaseUrl: window.gitBridgePublicBaseUrl,
|
||||
}
|
||||
const value = useMemo(() => {
|
||||
const value = {
|
||||
gitBridgePublicBaseUrl: window.gitBridgePublicBaseUrl,
|
||||
}
|
||||
|
||||
if (window.user.id) {
|
||||
applicationContextValue.user = window.user
|
||||
}
|
||||
if (window.user.id) {
|
||||
value.user = window.user
|
||||
}
|
||||
|
||||
return value
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<ApplicationContext.Provider value={applicationContextValue}>
|
||||
<ApplicationContext.Provider value={value}>
|
||||
{children}
|
||||
</ApplicationContext.Provider>
|
||||
)
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import React, { createContext, useCallback, useContext, useEffect } from 'react'
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import useScopeValue from './util/scope-value-hook'
|
||||
import useBrowserWindow from '../hooks/use-browser-window'
|
||||
|
@ -34,19 +40,23 @@ EditorContext.Provider.propTypes = {
|
|||
export function EditorProvider({ children, settings }) {
|
||||
const ide = useIdeContext()
|
||||
|
||||
const cobranding = window.brandVariation
|
||||
? {
|
||||
logoImgUrl: window.brandVariation.logo_url,
|
||||
brandVariationName: window.brandVariation.name,
|
||||
brandVariationId: window.brandVariation.id,
|
||||
brandId: window.brandVariation.brand_id,
|
||||
brandVariationHomeUrl: window.brandVariation.home_url,
|
||||
publishGuideHtml: window.brandVariation.publish_guide_html,
|
||||
partner: window.brandVariation.partner,
|
||||
brandedMenu: window.brandVariation.branded_menu,
|
||||
submitBtnHtml: window.brandVariation.submit_button_html,
|
||||
}
|
||||
: undefined
|
||||
const cobranding = useMemo(
|
||||
() =>
|
||||
window.brandVariation
|
||||
? {
|
||||
logoImgUrl: window.brandVariation.logo_url,
|
||||
brandVariationName: window.brandVariation.name,
|
||||
brandVariationId: window.brandVariation.id,
|
||||
brandId: window.brandVariation.brand_id,
|
||||
brandVariationHomeUrl: window.brandVariation.home_url,
|
||||
publishGuideHtml: window.brandVariation.publish_guide_html,
|
||||
partner: window.brandVariation.partner,
|
||||
brandedMenu: window.brandVariation.branded_menu,
|
||||
submitBtnHtml: window.brandVariation.submit_button_html,
|
||||
}
|
||||
: undefined,
|
||||
[]
|
||||
)
|
||||
|
||||
const [loading] = useScopeValue('state.loading')
|
||||
const [projectRootDocId] = useScopeValue('project.rootDoc_id')
|
||||
|
@ -87,23 +97,33 @@ export function EditorProvider({ children, settings }) {
|
|||
)
|
||||
}, [projectName, setTitle])
|
||||
|
||||
const editorContextValue = {
|
||||
cobranding,
|
||||
hasPremiumCompile: compileGroup === 'priority',
|
||||
loading,
|
||||
projectId: window.project_id,
|
||||
projectRootDocId,
|
||||
projectName: projectName || '', // initially might be empty in Angular
|
||||
renameProject,
|
||||
isProjectOwner: ownerId === window.user.id,
|
||||
isRestrictedTokenMember: window.isRestrictedTokenMember,
|
||||
rootFolder,
|
||||
}
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
cobranding,
|
||||
hasPremiumCompile: compileGroup === 'priority',
|
||||
loading,
|
||||
projectId: window.project_id,
|
||||
projectRootDocId,
|
||||
projectName: projectName || '', // initially might be empty in Angular
|
||||
renameProject,
|
||||
isProjectOwner: ownerId === window.user.id,
|
||||
isRestrictedTokenMember: window.isRestrictedTokenMember,
|
||||
rootFolder,
|
||||
}),
|
||||
[
|
||||
cobranding,
|
||||
compileGroup,
|
||||
loading,
|
||||
ownerId,
|
||||
projectName,
|
||||
projectRootDocId,
|
||||
renameProject,
|
||||
rootFolder,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<EditorContext.Provider value={editorContextValue}>
|
||||
{children}
|
||||
</EditorContext.Provider>
|
||||
<EditorContext.Provider value={value}>{children}</EditorContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { createContext, useContext, useCallback } from 'react'
|
||||
import React, { createContext, useContext, useCallback, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import useScopeValue from './util/scope-value-hook'
|
||||
import { useIdeContext } from './ide-context'
|
||||
|
@ -43,19 +43,32 @@ export function LayoutProvider({ children }) {
|
|||
'ui.reviewPanelOpen'
|
||||
)
|
||||
const [leftMenuShown, setLeftMenuShown] = useScopeValue('ui.leftMenuShown')
|
||||
const [pdfLayout] = useScopeValue('ui.pdfLayout')
|
||||
const [pdfLayout] = useScopeValue('ui.pdfLayout', $scope)
|
||||
|
||||
const value = {
|
||||
view,
|
||||
setView,
|
||||
chatIsOpen,
|
||||
setChatIsOpen,
|
||||
reviewPanelOpen,
|
||||
setReviewPanelOpen,
|
||||
leftMenuShown,
|
||||
setLeftMenuShown,
|
||||
pdfLayout,
|
||||
}
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
view,
|
||||
setView,
|
||||
chatIsOpen,
|
||||
setChatIsOpen,
|
||||
reviewPanelOpen,
|
||||
setReviewPanelOpen,
|
||||
leftMenuShown,
|
||||
setLeftMenuShown,
|
||||
pdfLayout,
|
||||
}),
|
||||
[
|
||||
chatIsOpen,
|
||||
leftMenuShown,
|
||||
pdfLayout,
|
||||
reviewPanelOpen,
|
||||
setChatIsOpen,
|
||||
setLeftMenuShown,
|
||||
setReviewPanelOpen,
|
||||
setView,
|
||||
view,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>
|
||||
|
|
Loading…
Add table
Reference in a new issue