-
Notifications
You must be signed in to change notification settings - Fork 673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: optimize robots and runs table ui #382
base: develop
Are you sure you want to change the base?
Conversation
WalkthroughThe pull request introduces performance optimizations and structural modifications to the Changes
Suggested labels
Suggested reviewers
Possibly related PRs
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (4)
src/components/robot/RecordingsTable.tsx (4)
501-509
: Reevaluate memoization of simple componentsMemoizing components like
TableCell
and the action buttons might not yield significant performance benefits, as these components are lightweight and may not cause performance issues. Over-memoization can add unnecessary complexity to the codebase.Consider removing unnecessary memoization:
- const MemoizedTableCell = memo(TableCell); - // Memoized action buttons - const MemoizedInterpretButton = memo(InterpretButton); - const MemoizedScheduleButton = memo(ScheduleButton); - const MemoizedIntegrateButton = memo(IntegrateButton); - const MemoizedSettingsButton = memo(SettingsButton); - const MemoizedOptionsButton = memo(OptionsButton);If profiling indicates that these components do not cause performance bottlenecks, it's preferable to use them without
memo
for cleaner code.
134-136
: Include dependencies in 'handleChangePage' useCallbackThe
handleChangePage
function usessetPage
, which is not included in the dependency array of theuseCallback
. While state setters likesetPage
are stable and don't strictly need to be included, adding them can improve code clarity and prevent potential issues if the function logic changes.Update the dependencies:
- }, []); + }, [setPage]);
148-175
: Ensure all dependencies are included in 'fetchRecordings' useCallbackThe
fetchRecordings
function usessetIsLoading
, but it's not listed in the dependency array. Although state setter functions are stable, including them can enhance readability and maintain consistency.Update the dependencies:
}, [setRecordings, notify, t + , setIsLoading ]);
255-258
: Simplify state management after deletionAfter successfully deleting a recording, the code calls
setRows([]);
followed byfetchRecordings();
. Clearingrows
before refetching may cause an unnecessary intermediate render. SincefetchRecordings
updatesrows
, you can remove thesetRows([]);
call to streamline the code.Apply this diff to simplify:
const success = await deleteRecordingFromStorage(id); if (success) { - setRows([]); notify('success', t('recordingtable.notifications.delete_success')); fetchRecordings(); }
const filteredRows = useMemo(() => { | ||
const searchLower = searchTerm.toLowerCase(); | ||
return searchTerm | ||
? rows.filter(row => row.name.toLowerCase().includes(searchLower)) | ||
: rows; | ||
}, [rows, debouncedSearchTerm]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use debounced search term for consistent filtering
In the filteredRows
computation, the filtering logic uses searchTerm
instead of debouncedSearchTerm
. This can cause the filter to react to every keystroke, negating the intended debounce effect. To ensure the filter updates correspond with the debounced input, use debouncedSearchTerm
in your filtering logic.
Apply this diff to fix the issue:
const filteredRows = useMemo(() => {
- const searchLower = searchTerm.toLowerCase();
- return searchTerm
+ const searchLower = debouncedSearchTerm.toLowerCase();
+ return debouncedSearchTerm
? rows.filter(row => row.name.toLowerCase().includes(searchLower))
: rows;
- }, [rows, debouncedSearchTerm]);
+ }, [rows, debouncedSearchTerm]);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const filteredRows = useMemo(() => { | |
const searchLower = searchTerm.toLowerCase(); | |
return searchTerm | |
? rows.filter(row => row.name.toLowerCase().includes(searchLower)) | |
: rows; | |
}, [rows, debouncedSearchTerm]); | |
const filteredRows = useMemo(() => { | |
const searchLower = debouncedSearchTerm.toLowerCase(); | |
return debouncedSearchTerm | |
? rows.filter(row => row.name.toLowerCase().includes(searchLower)) | |
: rows; | |
}, [rows, debouncedSearchTerm]); |
const TableRowMemoized = memo(({ row, columns, handlers }: any) => { | ||
return ( | ||
<TableRow hover role="checkbox" tabIndex={-1}> | ||
{columns.map((column: Column) => { | ||
const value: any = row[column.id]; | ||
if (value !== undefined) { | ||
return ( | ||
<MemoizedTableCell key={column.id} align={column.align}> | ||
{value} | ||
</MemoizedTableCell> | ||
); | ||
} else { | ||
switch (column.id) { | ||
case 'interpret': | ||
return ( | ||
<MemoizedTableCell key={column.id} align={column.align}> | ||
<MemoizedInterpretButton handleInterpret={() => handlers.handleRunRecording(row.id, row.name, row.params || [])} /> | ||
</MemoizedTableCell> | ||
); | ||
case 'schedule': | ||
return ( | ||
<MemoizedTableCell key={column.id} align={column.align}> | ||
<MemoizedScheduleButton handleSchedule={() => handlers.handleScheduleRecording(row.id, row.name, row.params || [])} /> | ||
</MemoizedTableCell> | ||
); | ||
case 'integrate': | ||
return ( | ||
<MemoizedTableCell key={column.id} align={column.align}> | ||
<MemoizedIntegrateButton handleIntegrate={() => handlers.handleIntegrateRecording(row.id, row.name, row.params || [])} /> | ||
</MemoizedTableCell> | ||
); | ||
case 'options': | ||
return ( | ||
<MemoizedTableCell key={column.id} align={column.align}> | ||
<MemoizedOptionsButton | ||
handleEdit={() => handlers.handleEditRobot(row.id, row.name, row.params || [])} | ||
handleDuplicate={() => handlers.handleDuplicateRobot(row.id, row.name, row.params || [])} | ||
handleDelete={() => handlers.handleDelete(row.id)} | ||
/> | ||
</MemoizedTableCell> | ||
); | ||
case 'settings': | ||
return ( | ||
<MemoizedTableCell key={column.id} align={column.align}> | ||
<MemoizedSettingsButton handleSettings={() => handlers.handleSettingsRecording(row.id, row.name, row.params || [])} /> | ||
</MemoizedTableCell> | ||
); | ||
default: | ||
return null; | ||
} | ||
} | ||
})} | ||
</TableRow> | ||
); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Define explicit types instead of using 'any'
The TableRowMemoized
component and its nested components use the any
type for props. This practice weakens type safety and can lead to runtime errors that TypeScript is designed to prevent. Defining explicit interfaces for your props enhances code reliability and maintainability.
Apply this diff to define appropriate types:
+ interface TableRowProps {
+ row: Data;
+ columns: Column[];
+ handlers: Handlers;
+ }
- const TableRowMemoized = memo(({ row, columns, handlers }: any) => {
+ const TableRowMemoized = memo(({ row, columns, handlers }: TableRowProps) => {
Similarly, define interfaces for Handlers
and ensure all components have proper typings to leverage TypeScript's advantages.
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/components/run/RunsTable.tsx (3)
101-115
: Consider using a custom debounce hookThe current implementation has a few areas for improvement:
- The
Function
type is too generic and could lead to type-safety issues- The debounce logic could be extracted into a reusable hook
Consider refactoring to use a custom hook:
interface DebouncedFunction<T extends (...args: any[]) => any> { (...args: Parameters<T>): void; } function useDebounce<T extends (...args: any[]) => any>( fn: T, delay: number ): DebouncedFunction<T> { const timeoutRef = React.useRef<NodeJS.Timeout>(); return React.useCallback( (...args: Parameters<T>) => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } timeoutRef.current = setTimeout(() => fn(...args), delay); }, [fn, delay] ); } // Usage const debouncedSetSearch = useDebounce((value: string) => { setSearchTerm(value); setPage(0); }, 300);🧰 Tools
🪛 Biome (1.9.4)
[error] 101-101: Don't use 'Function' as a type.
Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.
(lint/complexity/noBannedTypes)
117-135
: Enhance error handling with specific error messagesWhile the error handling is good, consider providing more specific error messages based on the error type.
Consider enhancing the error handling:
} catch (error) { - notify('error', t('runstable.notifications.fetch_error')); + const errorMessage = error instanceof Error + ? `${t('runstable.notifications.fetch_error')}: ${error.message}` + : t('runstable.notifications.fetch_error'); + notify('error', errorMessage); }
179-196
: Consider extracting CollapsibleRow as a memoized componentWhile the renderTableRows memoization is good, the CollapsibleRow component could be memoized separately for better granular control over re-renders.
Consider creating a memoized version of CollapsibleRow:
const MemoizedCollapsibleRow = React.memo(CollapsibleRow, (prev, next) => { return ( prev.row.id === next.row.id && prev.isOpen === next.isOpen && prev.currentLog === next.currentLog && prev.runningRecordingName === next.runningRecordingName ); });Then use it in renderTableRows:
- <CollapsibleRow + <MemoizedCollapsibleRow
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/run/RunsTable.tsx
(2 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/run/RunsTable.tsx
[error] 101-101: Don't use 'Function' as a type.
Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.
(lint/complexity/noBannedTypes)
🔇 Additional comments (3)
src/components/run/RunsTable.tsx (3)
72-78
: Good use of useMemo for translations!The memoization of translatedColumns prevents unnecessary recalculations when other state changes occur.
228-228
: Good performance optimization with unmountOnExit!Using
unmountOnExit
helps reduce the memory footprint by unmounting inactive accordion panels.
138-151
: Well-implemented cleanup in useEffect!Good use of the mounted flag to prevent memory leaks and proper cleanup on component unmount.
Enhance ui performance of recordings and runs table. Closes: #381
Summary by CodeRabbit
Performance Improvements
New Features
User Experience