Compare commits

..

2 Commits

Author SHA1 Message Date
18ec053e44 Merge pull request 'правки' (#47) from table-optimization-fix into test
Reviewed-on: #47
2026-07-24 17:28:19 +03:00
PotapovaA
e4726b6284 правки 2026-07-24 17:26:05 +03:00
3 changed files with 26 additions and 56 deletions

View File

@ -23,7 +23,7 @@ const FORMULA_INDICATOR_STYLES = {
const TEXTAREA_STYLES = { const TEXTAREA_STYLES = {
width: '100%', width: '100%',
height: 'auto', height: '3rem',
fieldSizing: 'content', fieldSizing: 'content',
fontSize: 'inherit', fontSize: 'inherit',
}; };
@ -32,7 +32,6 @@ const EditCell = memo(({ refCell, onChange, disabled, value, tableId, cellId, ta
const { endEditing: contextEndEditing } = useRealtime(); const { endEditing: contextEndEditing } = useRealtime();
const storageKey = `formula_${tableId}_${cellId}`; const storageKey = `formula_${tableId}_${cellId}`;
// Объединенное состояние
const [state, setState] = useState(() => ({ const [state, setState] = useState(() => ({
currentValue: value, currentValue: value,
displayValue: value, displayValue: value,
@ -40,7 +39,7 @@ const EditCell = memo(({ refCell, onChange, disabled, value, tableId, cellId, ta
})); }));
// Позиция ячейки // Позиция ячейки
const [position, setPosition] = useState({ left: 0, width: 0, translateY: '' }); const [position, setPosition] = useState();
const textareaRef = useRef(null); const textareaRef = useRef(null);
const refFinished = useRef(false); const refFinished = useRef(false);
@ -89,8 +88,8 @@ const EditCell = memo(({ refCell, onChange, disabled, value, tableId, cellId, ta
if (!el) return; if (!el) return;
const resize = () => { const resize = () => {
el.style.height = 'auto';
el.style.minHeight = '3rem'; el.style.minHeight = '3rem';
el.style.height = '3rem';
el.style.height = el.scrollHeight + 'px'; el.style.height = el.scrollHeight + 'px';
el.style.maxHeight = '180px'; el.style.maxHeight = '180px';
}; };
@ -102,16 +101,6 @@ const EditCell = memo(({ refCell, onChange, disabled, value, tableId, cellId, ta
return () => observer.disconnect(); return () => observer.disconnect();
}, [state.currentValue]); }, [state.currentValue]);
// Фокус при монтировании
useEffect(() => {
const el = textareaRef.current;
if (el && !disabled) {
el.focus();
el.setSelectionRange(el.value.length, el.value.length);
}
}, [disabled]);
// Сохранение
const handleSave = useCallback(() => { const handleSave = useCallback(() => {
const { currentValue } = state; const { currentValue } = state;
@ -146,7 +135,6 @@ const EditCell = memo(({ refCell, onChange, disabled, value, tableId, cellId, ta
} }
}, [state, storageKey, onChange]); }, [state, storageKey, onChange]);
// Завершение редактирования
const finishEditing = useCallback(() => { const finishEditing = useCallback(() => {
if (refFinished.current) return; if (refFinished.current) return;
refFinished.current = true; refFinished.current = true;
@ -170,7 +158,6 @@ const EditCell = memo(({ refCell, onChange, disabled, value, tableId, cellId, ta
return () => document.removeEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside);
}, [finishEditing]); }, [finishEditing]);
// Обработчики событий
const handleChange = useCallback((e) => { const handleChange = useCallback((e) => {
const value = e.target.value; const value = e.target.value;
setState(prev => ({ setState(prev => ({
@ -187,12 +174,11 @@ const EditCell = memo(({ refCell, onChange, disabled, value, tableId, cellId, ta
} }
}, [finishEditing]); }, [finishEditing]);
// Мемоизация стилей
const editorStyles = useMemo(() => ({ const editorStyles = useMemo(() => ({
...EDITOR_STYLES, ...EDITOR_STYLES,
left: position.left, left: position?.left || 0,
width: position.width, width: position?.width || 0,
transform: position.translateY, transform: position?.translateY || 0,
}), [position]); }), [position]);
const textareaStyles = useMemo(() => ({ const textareaStyles = useMemo(() => ({
@ -204,6 +190,7 @@ const EditCell = memo(({ refCell, onChange, disabled, value, tableId, cellId, ta
return ( return (
<tr> <tr>
<td> <td>
{position &&
<div style={editorStyles}> <div style={editorStyles}>
{state.isFormulaMode && ( {state.isFormulaMode && (
<div style={FORMULA_INDICATOR_STYLES}>fx</div> <div style={FORMULA_INDICATOR_STYLES}>fx</div>
@ -218,6 +205,8 @@ const EditCell = memo(({ refCell, onChange, disabled, value, tableId, cellId, ta
placeholder={state.isFormulaMode ? 'Введите формулу (начинается с =)' : ''} placeholder={state.isFormulaMode ? 'Введите формулу (начинается с =)' : ''}
/> />
</div> </div>
}
</td> </td>
</tr> </tr>
); );

View File

@ -1,4 +1,3 @@
// components/RealtimeTable/index.js
import React, { useState, useMemo, useRef, useEffect, useCallback, useTransition } from 'react'; import React, { useState, useMemo, useRef, useEffect, useCallback, useTransition } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { import {

View File

@ -1,10 +1,8 @@
// getTableColumns.js
import React, { useEffect, useRef, useState, useMemo, useCallback } from 'react'; import React, { useEffect, useRef, useState, useMemo, useCallback } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { useParams } from 'react-router'; import { useParams } from 'react-router';
import { useRealtime } from './contexts/RealtimeContext'; import { useRealtime } from './contexts/RealtimeContext';
// Мемоизированный компонент EditCellPortal
const EditCellPortal = React.memo(({ const EditCellPortal = React.memo(({
cell, cell,
table, table,
@ -22,7 +20,6 @@ const EditCellPortal = React.memo(({
const { endEditing: contextEndEditing } = useRealtime(); const { endEditing: contextEndEditing } = useRealtime();
// Оптимизация: вычисляем позицию только при монтировании и изменении cell
useEffect(() => { useEffect(() => {
if (ref.current) { if (ref.current) {
const tbodyRef = ref.current.offsetParent?.offsetParent?.offsetParent; const tbodyRef = ref.current.offsetParent?.offsetParent?.offsetParent;
@ -30,9 +27,8 @@ const EditCellPortal = React.memo(({
setRefTbody(tbodyRef); setRefTbody(tbodyRef);
} }
} }
}, [refTbody]); // Добавляем refTbody в зависимости }, []);
// Мемоизация обработчика изменения
const handleChange = useCallback( (val) => { const handleChange = useCallback( (val) => {
table.setEditingCell(null); table.setEditingCell(null);
try { try {
@ -53,7 +49,6 @@ const EditCellPortal = React.memo(({
} }
}, [table, cell, onError]); }, [table, cell, onError]);
// Мемоизация пропсов для EditCell
const editCellProps = useMemo(() => ({ const editCellProps = useMemo(() => ({
refCell: ref, refCell: ref,
cell, cell,
@ -65,7 +60,6 @@ const EditCellPortal = React.memo(({
cellId: `${cell.row.id}_${cell.column.id}`, cellId: `${cell.row.id}_${cell.column.id}`,
}), [cell, isEditable, handleChange, tableKey, table]); }), [cell, isEditable, handleChange, tableKey, table]);
// Мемоизация портала
const portalContent = useMemo(() => { const portalContent = useMemo(() => {
if (!refTbody || isSaving) return null; if (!refTbody || isSaving) return null;
return createPortal( return createPortal(
@ -82,9 +76,7 @@ const EditCellPortal = React.memo(({
); );
}); });
EditCellPortal.displayName = 'EditCellPortal';
// Основная функция getTableColumns
export const getTableColumns = ({ export const getTableColumns = ({
Cell, Cell,
EditCell, EditCell,
@ -95,11 +87,9 @@ export const getTableColumns = ({
const columnColors = { ...columnsConfig.colors }; const columnColors = { ...columnsConfig.colors };
const columns = structuredClone(columnsConfig.columns); const columns = structuredClone(columnsConfig.columns);
// Кэши для вычисленных значений
const cellPropsCache = new WeakMap(); const cellPropsCache = new WeakMap();
const editPropsCache = new WeakMap(); const editPropsCache = new WeakMap();
// Функция для получения кешированных пропсов Cell
const getCachedCellProps = (row, column, table) => { const getCachedCellProps = (row, column, table) => {
const key = `${row.id}_${column.id}`; const key = `${row.id}_${column.id}`;
@ -109,7 +99,6 @@ export const getTableColumns = ({
const rowCache = cellPropsCache.get(row); const rowCache = cellPropsCache.get(row);
if (rowCache[key]) { if (rowCache[key]) {
// Проверяем, не изменились ли фильтры
const cached = rowCache[key]; const cached = rowCache[key];
const currentGlobalFilter = table.getState().globalFilter; const currentGlobalFilter = table.getState().globalFilter;
const currentColumnFilter = table.getState().columnFilters?.find(f => f.id === column.id)?.value; const currentColumnFilter = table.getState().columnFilters?.find(f => f.id === column.id)?.value;
@ -136,7 +125,6 @@ export const getTableColumns = ({
return props; return props;
}; };
// Функция для получения кешированных пропсов Edit
const getCachedEditProps = (row, column, table) => { const getCachedEditProps = (row, column, table) => {
const key = `${row.id}_${column.id}`; const key = `${row.id}_${column.id}`;
@ -160,11 +148,9 @@ export const getTableColumns = ({
// Оптимизированная функция processColumns // Оптимизированная функция processColumns
const processColumns = (columns) => { const processColumns = (columns) => {
columns.forEach((col) => { columns.forEach((col) => {
// Оптимизированный Cell
col.Cell = ({ cell, table, column, row }) => { col.Cell = ({ cell, table, column, row }) => {
const props = getCachedCellProps(row, column, table); const props = getCachedCellProps(row, column, table);
// Используем useMemo для мемоизации пропсов
const cellProps = useMemo(() => ({ const cellProps = useMemo(() => ({
row, row,
column, column,
@ -175,11 +161,9 @@ export const getTableColumns = ({
return <Cell {...cellProps} />; return <Cell {...cellProps} />;
}; };
// Оптимизированный Edit
col.Edit = ({ cell, table, row, column }) => { col.Edit = ({ cell, table, row, column }) => {
const props = getCachedEditProps(row, column, table); const props = getCachedEditProps(row, column, table);
// Мемоизируем компонент EditCellPortal
const editComponent = useMemo(() => ( const editComponent = useMemo(() => (
<EditCellPortal <EditCellPortal
cell={cell} cell={cell}
@ -193,7 +177,6 @@ export const getTableColumns = ({
return editComponent; return editComponent;
}; };
// Рекурсивно обрабатываем вложенные колонки
if (col.columns?.length) { if (col.columns?.length) {
processColumns(col.columns); processColumns(col.columns);
} }
@ -202,7 +185,6 @@ export const getTableColumns = ({
processColumns(columns); processColumns(columns);
// Оптимизированная колонка с номерами строк
const rowNumber = { const rowNumber = {
accessorKey: 'sort_order', accessorKey: 'sort_order',
header: '#', header: '#',