Optimize performance through memoization

This commit is contained in:
IRBorisov 2024-04-30 16:06:25 +03:00
parent ee9db2fb05
commit 266db4933a
7 changed files with 131 additions and 74 deletions

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import clsx from 'clsx'; import clsx from 'clsx';
import { useLayoutEffect, useState } from 'react'; import { useLayoutEffect, useMemo, useState } from 'react';
import { TabList, TabPanel, Tabs } from 'react-tabs'; import { TabList, TabPanel, Tabs } from 'react-tabs';
import BadgeHelp from '@/components/man/BadgeHelp'; import BadgeHelp from '@/components/man/BadgeHelp';
@ -100,6 +100,28 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
}); });
}, [substitutes.arguments, template.prototype, updateConstituenta, updateSubstitutes]); }, [substitutes.arguments, template.prototype, updateConstituenta, updateSubstitutes]);
const templatePanel = useMemo(
() => <TemplateTab state={template} partialUpdate={updateTemplate} />,
[template, updateTemplate]
);
const argumentsPanel = useMemo(
() => <ArgumentsTab schema={schema} state={substitutes} partialUpdate={updateSubstitutes} />,
[schema, substitutes, updateSubstitutes]
);
const editorPanel = useMemo(
() => (
<FormCreateCst
state={constituenta}
partialUpdate={updateConstituenta}
schema={schema}
setValidated={setValidated}
/>
),
[constituenta, updateConstituenta, schema]
);
return ( return (
<Modal <Modal
header='Создание конституенты из шаблона' header='Создание конституенты из шаблона'
@ -125,21 +147,12 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
<TabLabel label='Конституента' title='Редактирование атрибутов конституенты' className='w-[8rem]' /> <TabLabel label='Конституента' title='Редактирование атрибутов конституенты' className='w-[8rem]' />
</TabList> </TabList>
<TabPanel style={{ display: activeTab === TabID.TEMPLATE ? '' : 'none' }}> <TabPanel style={{ display: activeTab === TabID.TEMPLATE ? '' : 'none' }}>{templatePanel}</TabPanel>
<TemplateTab state={template} partialUpdate={updateTemplate} />
</TabPanel>
<TabPanel style={{ display: activeTab === TabID.ARGUMENTS ? '' : 'none' }}> <TabPanel style={{ display: activeTab === TabID.ARGUMENTS ? '' : 'none' }}>{argumentsPanel}</TabPanel>
<ArgumentsTab schema={schema} state={substitutes} partialUpdate={updateSubstitutes} />
</TabPanel>
<TabPanel className='cc-column' style={{ display: activeTab === TabID.CONSTITUENTA ? '' : 'none' }}> <TabPanel className='cc-column' style={{ display: activeTab === TabID.CONSTITUENTA ? '' : 'none' }}>
<FormCreateCst {editorPanel}
state={constituenta}
partialUpdate={updateConstituenta}
schema={schema}
setValidated={setValidated}
/>
</TabPanel> </TabPanel>
</Tabs> </Tabs>
</Modal> </Modal>

View File

@ -64,13 +64,8 @@ function DlgEditVersions({ hideWindow, versions, onDelete, onUpdate }: DlgEditVe
setDescription(selected?.description ?? ''); setDescription(selected?.description ?? '');
}, [selected]); }, [selected]);
return ( const versionsTable = useMemo(
<Modal () => (
readonly
header='Редактирование версий'
hideWindow={hideWindow}
className='flex flex-col w-[40rem] px-6 gap-3 pb-6'
>
<VersionsTable <VersionsTable
processing={processing} processing={processing}
items={versions} items={versions}
@ -78,6 +73,18 @@ function DlgEditVersions({ hideWindow, versions, onDelete, onUpdate }: DlgEditVe
onSelect={versionID => setSelected(versions.find(ver => ver.id === versionID))} onSelect={versionID => setSelected(versions.find(ver => ver.id === versionID))}
selected={selected?.id} selected={selected?.id}
/> />
),
[processing, versions, onDelete, selected?.id]
);
return (
<Modal
readonly
header='Редактирование версий'
hideWindow={hideWindow}
className='flex flex-col w-[40rem] px-6 gap-3 pb-6'
>
{versionsTable}
<div className='flex'> <div className='flex'>
<TextInput <TextInput
id='dlg_version' id='dlg_version'

View File

@ -58,6 +58,27 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
setSubstitutions([]); setSubstitutions([]);
}, [source.schema]); }, [source.schema]);
const schemaPanel = useMemo(() => <SchemaTab selected={donorID} setSelected={setDonorID} />, [donorID]);
const itemsPanel = useMemo(
() => (
<ConstituentsTab schema={source.schema} loading={source.loading} selected={selected} setSelected={setSelected} />
),
[source.schema, source.loading, selected]
);
const substitutesPanel = useMemo(
() => (
<SubstitutionsTab
receiver={receiver}
source={source.schema}
selected={selected}
loading={source.loading}
substitutions={substitutions}
setSubstitutions={setSubstitutions}
/>
),
[source.schema, source.loading, receiver, selected, substitutions]
);
return ( return (
<Modal <Modal
header='Импорт концептуальной схем' header='Импорт концептуальной схем'
@ -80,29 +101,9 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
<TabLabel label='Отождествления' title='Таблица отождествлений' className='w-[8rem]' /> <TabLabel label='Отождествления' title='Таблица отождествлений' className='w-[8rem]' />
</TabList> </TabList>
<TabPanel style={{ display: activeTab === TabID.SCHEMA ? '' : 'none' }}> <TabPanel style={{ display: activeTab === TabID.SCHEMA ? '' : 'none' }}>{schemaPanel}</TabPanel>
<SchemaTab selected={donorID} setSelected={setDonorID} /> <TabPanel style={{ display: activeTab === TabID.SELECTIONS ? '' : 'none' }}>{itemsPanel}</TabPanel>
</TabPanel> <TabPanel style={{ display: activeTab === TabID.SUBSTITUTIONS ? '' : 'none' }}>{substitutesPanel}</TabPanel>
<TabPanel style={{ display: activeTab === TabID.SELECTIONS ? '' : 'none' }}>
<ConstituentsTab
schema={source.schema}
loading={source.loading}
selected={selected}
setSelected={setSelected}
/>
</TabPanel>
<TabPanel style={{ display: activeTab === TabID.SUBSTITUTIONS ? '' : 'none' }}>
<SubstitutionsTab
receiver={receiver}
source={source.schema}
selected={selected}
loading={source.loading}
substitutions={substitutions}
setSubstitutions={setSubstitutions}
/>
</TabPanel>
</Tabs> </Tabs>
</Modal> </Modal>
); );

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useCallback, useLayoutEffect, useState } from 'react'; import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import DataLoader from '@/components/wrap/DataLoader'; import DataLoader from '@/components/wrap/DataLoader';
@ -61,6 +61,16 @@ function LibraryPage() {
setFilter({}); setFilter({});
}, []); }, []);
const view = useMemo(
() => (
<ViewLibrary
resetQuery={resetQuery} //
items={items}
/>
),
[resetQuery, items]
);
return ( return (
<DataLoader <DataLoader
id='library-page' // id='library-page' //
@ -76,10 +86,7 @@ function LibraryPage() {
filtered={items.length} filtered={items.length}
setFilter={setFilter} setFilter={setFilter}
/> />
<ViewLibrary {view}
resetQuery={resetQuery} //
items={items}
/>
</DataLoader> </DataLoader>
); );
} }

View File

@ -2,7 +2,7 @@
import { ReactCodeMirrorRef } from '@uiw/react-codemirror'; import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
import { AnimatePresence } from 'framer-motion'; import { AnimatePresence } from 'framer-motion';
import { useCallback, useLayoutEffect, useRef, useState } from 'react'; import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { FaRegKeyboard } from 'react-icons/fa6'; import { FaRegKeyboard } from 'react-icons/fa6';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@ -152,6 +152,17 @@ function EditorRSExpression({
setMathFont(mathFont === 'math' ? 'math2' : 'math'); setMathFont(mathFont === 'math' ? 'math2' : 'math');
} }
const controls = useMemo(
() => (
<RSEditorControls
isOpen={showControls && (!disabled || model.processing)}
disabled={disabled}
onEdit={handleEdit}
/>
),
[showControls, disabled, model.processing, handleEdit]
);
return ( return (
<div> <div>
<AnimatePresence> <AnimatePresence>
@ -208,11 +219,7 @@ function EditorRSExpression({
{...restProps} {...restProps}
/> />
<RSEditorControls {controls}
isOpen={showControls && (!disabled || model.processing)}
disabled={disabled}
onEdit={handleEdit}
/>
<ParsingResult <ParsingResult
isOpen={!!parser.parseData && parser.parseData.errors.length > 0} isOpen={!!parser.parseData && parser.parseData.errors.length > 0}

View File

@ -164,6 +164,30 @@ function RSTabs() {
const panelHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]); const panelHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]);
const cardPanel = useMemo(
() => (
<EditorRSForm
isModified={isModified} // prettier: split lines
setIsModified={setIsModified}
onDestroy={onDestroySchema}
/>
),
[isModified, onDestroySchema]
);
const listPanel = useMemo(() => <EditorRSList onOpenEdit={onOpenCst} />, [onOpenCst]);
const editorPanel = useMemo(
() => (
<EditorConstituenta
isModified={isModified}
setIsModified={setIsModified}
activeCst={activeCst}
onOpenEdit={onOpenCst}
/>
),
[isModified, setIsModified, activeCst, onOpenCst]
);
return ( return (
<RSEditState <RSEditState
selected={selected} selected={selected}
@ -198,24 +222,15 @@ function RSTabs() {
<AnimateFade className='overflow-y-auto' style={{ maxHeight: panelHeight }}> <AnimateFade className='overflow-y-auto' style={{ maxHeight: panelHeight }}>
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '' : 'none' }}> <TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '' : 'none' }}>
<EditorRSForm {cardPanel}
isModified={isModified} // prettier: split lines
setIsModified={setIsModified}
onDestroy={onDestroySchema}
/>
</TabPanel> </TabPanel>
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_LIST ? '' : 'none' }}> <TabPanel forceRender style={{ display: activeTab === RSTabID.CST_LIST ? '' : 'none' }}>
<EditorRSList onOpenEdit={onOpenCst} /> {listPanel}
</TabPanel> </TabPanel>
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_EDIT ? '' : 'none' }}> <TabPanel forceRender style={{ display: activeTab === RSTabID.CST_EDIT ? '' : 'none' }}>
<EditorConstituenta {editorPanel}
isModified={isModified}
setIsModified={setIsModified}
activeCst={activeCst}
onOpenEdit={onOpenCst}
/>
</TabPanel> </TabPanel>
<TabPanel style={{ display: activeTab === RSTabID.TERM_GRAPH ? '' : 'none' }}> <TabPanel style={{ display: activeTab === RSTabID.TERM_GRAPH ? '' : 'none' }}>

View File

@ -2,7 +2,7 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { useState } from 'react'; import { useMemo, useState } from 'react';
import { useConceptOptions } from '@/context/OptionsContext'; import { useConceptOptions } from '@/context/OptionsContext';
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform'; import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
@ -27,6 +27,19 @@ function ViewConstituents({ expression, schema, activeID, isBottom, onOpenEdit }
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema?.items ?? []); const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema?.items ?? []);
const table = useMemo(
() => (
<ConstituentsTable
maxHeight={isBottom ? '12rem' : calculateHeight('8.2rem')}
items={filteredData}
activeID={activeID}
onOpenEdit={onOpenEdit}
denseThreshold={COLUMN_EXPRESSION_HIDE_THRESHOLD}
/>
),
[isBottom, filteredData, activeID, onOpenEdit, calculateHeight]
);
return ( return (
<motion.div <motion.div
className={clsx( className={clsx(
@ -46,13 +59,7 @@ function ViewConstituents({ expression, schema, activeID, isBottom, onOpenEdit }
activeExpression={expression} activeExpression={expression}
setFiltered={setFilteredData} setFiltered={setFilteredData}
/> />
<ConstituentsTable {table}
maxHeight={isBottom ? '12rem' : calculateHeight('8.2rem')}
items={filteredData}
activeID={activeID}
onOpenEdit={onOpenEdit}
denseThreshold={COLUMN_EXPRESSION_HIDE_THRESHOLD}
/>
</motion.div> </motion.div>
); );
} }