From cad3b4c9057c22fdc1564ec82ba49b13851f70ac Mon Sep 17 00:00:00 2001 From: jeffvli Date: Sat, 17 Jan 2026 22:01:24 -0800 Subject: [PATCH] fix stale updates in Grid/Table config --- .../shared/components/grid-config.tsx | 158 +++++------ .../shared/components/table-config.tsx | 249 ++++++++---------- 2 files changed, 172 insertions(+), 235 deletions(-) diff --git a/src/renderer/features/shared/components/grid-config.tsx b/src/renderer/features/shared/components/grid-config.tsx index d1f002c85..605d7afd3 100644 --- a/src/renderer/features/shared/components/grid-config.tsx +++ b/src/renderer/features/shared/components/grid-config.tsx @@ -11,7 +11,7 @@ import { import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview'; import clsx from 'clsx'; import Fuse, { FuseResultMatch } from 'fuse.js'; -import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import styles from './table-config.module.css'; @@ -287,8 +287,7 @@ export const GridConfig = ({ setList(listKey, { ...list, grid: { ...grid, rows } })} + onChange={(rows) => setList(listKey, { grid: { rows } })} value={grid.rows} /> @@ -297,17 +296,23 @@ export const GridConfig = ({ const GridRowConfig = ({ data, - listKey, onChange, value, }: { data: { label: string; value: string }[]; - listKey: ItemListKey; onChange: (value: ItemGridListRowConfig[]) => void; value: ItemGridListRowConfig[]; }) => { const { t } = useTranslation(); + const valueRef = useRef(value); + const onChangeRef = useRef(onChange); + + useLayoutEffect(() => { + valueRef.current = value; + onChangeRef.current = onChange; + }); + const labelMap = useMemo(() => { return data.reduce( (acc, item) => { @@ -318,79 +323,55 @@ const GridRowConfig = ({ ); }, [data]); - const handleChangeEnabled = useCallback( - (item: ItemGridListRowConfig, checked: boolean) => { - const value = useSettingsStore.getState().lists[listKey]?.grid.rows; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; - newValues[index] = { ...newValues[index], isEnabled: checked }; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleChangeEnabled = useCallback((item: ItemGridListRowConfig, checked: boolean) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; + newValues[index] = { ...newValues[index], isEnabled: checked }; + onChangeRef.current(newValues); + }, []); - const handleMoveUp = useCallback( - (item: ItemGridListRowConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.grid.rows; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - if (index === 0) return; - const newValues = [...value]; - [newValues[index], newValues[index - 1]] = [newValues[index - 1], newValues[index]]; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleMoveUp = useCallback((item: ItemGridListRowConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + if (index === 0) return; + const newValues = [...currentValue]; + [newValues[index], newValues[index - 1]] = [newValues[index - 1], newValues[index]]; + onChangeRef.current(newValues); + }, []); - const handleMoveDown = useCallback( - (item: ItemGridListRowConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.grid.rows; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - if (index === value.length - 1) return; - const newValues = [...value]; - [newValues[index], newValues[index + 1]] = [newValues[index + 1], newValues[index]]; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleMoveDown = useCallback((item: ItemGridListRowConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + if (index === currentValue.length - 1) return; + const newValues = [...currentValue]; + [newValues[index], newValues[index + 1]] = [newValues[index + 1], newValues[index]]; + onChangeRef.current(newValues); + }, []); - const handleAlignLeft = useCallback( - (item: ItemGridListRowConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.grid.rows; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; - newValues[index] = { ...newValues[index], align: 'start' }; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleAlignLeft = useCallback((item: ItemGridListRowConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; + newValues[index] = { ...newValues[index], align: 'start' }; + onChangeRef.current(newValues); + }, []); - const handleAlignCenter = useCallback( - (item: ItemGridListRowConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.grid.rows; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; - newValues[index] = { ...newValues[index], align: 'center' }; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleAlignCenter = useCallback((item: ItemGridListRowConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; + newValues[index] = { ...newValues[index], align: 'center' }; + onChangeRef.current(newValues); + }, []); - const handleAlignRight = useCallback( - (item: ItemGridListRowConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.grid.rows; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; - newValues[index] = { ...newValues[index], align: 'end' }; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleAlignRight = useCallback((item: ItemGridListRowConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; + newValues[index] = { ...newValues[index], align: 'end' }; + onChangeRef.current(newValues); + }, []); const [searchRows, setSearchRows] = useDebouncedState('', 300); @@ -420,25 +401,20 @@ const GridRowConfig = ({ })); }, [value, searchRows, fuse]); - const handleReorder = useCallback( - (idFrom: string, idTo: string, edge: Edge | null) => { - const currentValue = useSettingsStore.getState().lists[listKey]?.grid.rows; - if (!currentValue) return; + const handleReorder = useCallback((idFrom: string, idTo: string, edge: Edge | null) => { + const currentValue = valueRef.current; + const idList = currentValue.map((item) => item.id); + const newIdOrder = dndUtils.reorderById({ + edge, + idFrom, + idTo, + list: idList, + }); - const idList = currentValue.map((item) => item.id); - const newIdOrder = dndUtils.reorderById({ - edge, - idFrom, - idTo, - list: idList, - }); - - // Map the new ID order back to full items - const newOrder = newIdOrder.map((id) => currentValue.find((item) => item.id === id)!); - onChange(newOrder); - }, - [listKey, onChange], - ); + // Map the new ID order back to full items + const newOrder = newIdOrder.map((id) => currentValue.find((item) => item.id === id)!); + onChangeRef.current(newOrder); + }, []); return ( diff --git a/src/renderer/features/shared/components/table-config.tsx b/src/renderer/features/shared/components/table-config.tsx index 02e122fdf..125a609ff 100644 --- a/src/renderer/features/shared/components/table-config.tsx +++ b/src/renderer/features/shared/components/table-config.tsx @@ -11,7 +11,7 @@ import { import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview'; import clsx from 'clsx'; import Fuse, { type FuseResultMatch } from 'fuse.js'; -import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import styles from './table-config.module.css'; @@ -252,10 +252,7 @@ export const TableConfig = ({ - setList(listKey, { ...list, table: { ...list.table, columns } }) - } + onChange={(columns) => setList(listKey, { table: { columns } })} value={list.table.columns} /> @@ -264,17 +261,23 @@ export const TableConfig = ({ const TableColumnConfig = ({ data, - listKey, onChange, value, }: { data: { label: string; value: string }[]; - listKey: ItemListKey; onChange: (value: ItemTableListColumnConfig[]) => void; value: ItemTableListColumnConfig[]; }) => { const { t } = useTranslation(); + const valueRef = useRef(value); + const onChangeRef = useRef(onChange); + + useLayoutEffect(() => { + valueRef.current = value; + onChangeRef.current = onChange; + }); + const labelMap = useMemo(() => { return data.reduce( (acc, item) => { @@ -285,133 +288,97 @@ const TableColumnConfig = ({ ); }, [data]); - const handleChangeEnabled = useCallback( - (item: ItemTableListColumnConfig, checked: boolean) => { - const value = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; - newValues[index] = { ...newValues[index], isEnabled: checked }; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleChangeEnabled = useCallback((item: ItemTableListColumnConfig, checked: boolean) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; + newValues[index] = { ...newValues[index], isEnabled: checked }; + onChangeRef.current(newValues); + }, []); - const handleMoveUp = useCallback( - (item: ItemTableListColumnConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - if (index === 0) return; - const newValues = [...value]; - [newValues[index], newValues[index - 1]] = [newValues[index - 1], newValues[index]]; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleMoveUp = useCallback((item: ItemTableListColumnConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + if (index === 0) return; + const newValues = [...currentValue]; + [newValues[index], newValues[index - 1]] = [newValues[index - 1], newValues[index]]; + onChangeRef.current(newValues); + }, []); - const handleMoveDown = useCallback( - (item: ItemTableListColumnConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - if (index === value.length - 1) return; - const newValues = [...value]; - [newValues[index], newValues[index + 1]] = [newValues[index + 1], newValues[index]]; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleMoveDown = useCallback((item: ItemTableListColumnConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + if (index === currentValue.length - 1) return; + const newValues = [...currentValue]; + [newValues[index], newValues[index + 1]] = [newValues[index + 1], newValues[index]]; + onChangeRef.current(newValues); + }, []); - const handlePinToLeft = useCallback( - (item: ItemTableListColumnConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; + const handlePinToLeft = useCallback((item: ItemTableListColumnConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; - const isPinned = newValues[index].pinned; - const isPinnedLeft = isPinned === 'left'; + const isPinned = newValues[index].pinned; + const isPinnedLeft = isPinned === 'left'; - if (isPinnedLeft) { - newValues[index] = { ...newValues[index], pinned: null }; - } else { - newValues[index] = { ...newValues[index], pinned: 'left' }; - } + if (isPinnedLeft) { + newValues[index] = { ...newValues[index], pinned: null }; + } else { + newValues[index] = { ...newValues[index], pinned: 'left' }; + } - onChange(newValues); - }, - [listKey, onChange], - ); + onChangeRef.current(newValues); + }, []); - const handlePinToRight = useCallback( - (item: ItemTableListColumnConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; + const handlePinToRight = useCallback((item: ItemTableListColumnConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; - const isPinned = newValues[index].pinned; - const isPinnedRight = isPinned === 'right'; + const isPinned = newValues[index].pinned; + const isPinnedRight = isPinned === 'right'; - if (isPinnedRight) { - newValues[index] = { ...newValues[index], pinned: null }; - } else { - newValues[index] = { ...newValues[index], pinned: 'right' }; - } + if (isPinnedRight) { + newValues[index] = { ...newValues[index], pinned: null }; + } else { + newValues[index] = { ...newValues[index], pinned: 'right' }; + } - onChange(newValues); - }, - [listKey, onChange], - ); + onChangeRef.current(newValues); + }, []); - const handleAlignLeft = useCallback( - (item: ItemTableListColumnConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; - newValues[index] = { ...newValues[index], align: 'start' }; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleAlignLeft = useCallback((item: ItemTableListColumnConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; + newValues[index] = { ...newValues[index], align: 'start' }; + onChangeRef.current(newValues); + }, []); - const handleAlignCenter = useCallback( - (item: ItemTableListColumnConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; - newValues[index] = { ...newValues[index], align: 'center' }; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleAlignCenter = useCallback((item: ItemTableListColumnConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; + newValues[index] = { ...newValues[index], align: 'center' }; + onChangeRef.current(newValues); + }, []); - const handleAlignRight = useCallback( - (item: ItemTableListColumnConfig) => { - const value = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; - newValues[index] = { ...newValues[index], align: 'end' }; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleAlignRight = useCallback((item: ItemTableListColumnConfig) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; + newValues[index] = { ...newValues[index], align: 'end' }; + onChangeRef.current(newValues); + }, []); - const handleAutoSize = useCallback( - (item: ItemTableListColumnConfig, checked: boolean) => { - const value = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; - newValues[index] = { ...newValues[index], autoSize: checked }; - onChange(newValues); - }, - [listKey, onChange], - ); + const handleAutoSize = useCallback((item: ItemTableListColumnConfig, checked: boolean) => { + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; + newValues[index] = { ...newValues[index], autoSize: checked }; + onChangeRef.current(newValues); + }, []); const handleRowWidth = useCallback( (item: ItemTableListColumnConfig, number: number | string) => { @@ -427,14 +394,13 @@ const TableColumnConfig = ({ number = 2000; } - const value = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!value) return; - const index = value.findIndex((v) => v.id === item.id); - const newValues = [...value]; + const currentValue = valueRef.current; + const index = currentValue.findIndex((v) => v.id === item.id); + const newValues = [...currentValue]; newValues[index] = { ...newValues[index], width: number }; - onChange(newValues); + onChangeRef.current(newValues); }, - [listKey, onChange], + [], ); const [searchColumns, setSearchColumns] = useDebouncedState('', 300); @@ -465,25 +431,20 @@ const TableColumnConfig = ({ })); }, [value, searchColumns, fuse]); - const handleReorder = useCallback( - (idFrom: string, idTo: string, edge: Edge | null) => { - const currentValue = useSettingsStore.getState().lists[listKey]?.table.columns; - if (!currentValue) return; + const handleReorder = useCallback((idFrom: string, idTo: string, edge: Edge | null) => { + const currentValue = valueRef.current; + const idList = currentValue.map((item) => item.id); + const newIdOrder = dndUtils.reorderById({ + edge, + idFrom, + idTo, + list: idList, + }); - const idList = currentValue.map((item) => item.id); - const newIdOrder = dndUtils.reorderById({ - edge, - idFrom, - idTo, - list: idList, - }); - - // Map the new ID order back to full items - const newOrder = newIdOrder.map((id) => currentValue.find((item) => item.id === id)!); - onChange(newOrder); - }, - [listKey, onChange], - ); + // Map the new ID order back to full items + const newOrder = newIdOrder.map((id) => currentValue.find((item) => item.id === id)!); + onChangeRef.current(newOrder); + }, []); return (