perf: memoize expensive list computations + memo on grid/list items
Results.tsx: - useMemo: filter + sort in one pass (was 7 separate array iterations per render) - useMemo: platform/maison/future/missingData counts in single for-loop - Fix: move queryClient.invalidateQueries from render body into useEffect Properties.tsx: - useMemo: wrap applyFilters() call (was full copy+sort on every render) - useMemo: compute matchReady/criticalGaps/lowConfidence/staleOrOutdated/ allMissingFields in a single for-loop (was 5 separate filter passes) ReminderFeed.tsx: - useMemo: wrap applyFilters() call - useCallback: resetFilters (passed to ReminderEmptyState) PropertyIntelligenceCard, ReminderListRow: - React.memo: grid/list items no longer re-render when unrelated parent state changes (e.g. selectedId, filter UI state) tsc --noEmit passes with zero errors Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { memo } from 'react'
|
||||
import { Box, Button, Chip, LinearProgress, Typography } from '@mui/material'
|
||||
import { LocationPreview } from '../shared/LocationPreview'
|
||||
import { getAssetTypeColor, getAssetTypeLabel, getAvailabilityLabel } from './propertyHelpers'
|
||||
@@ -14,7 +15,7 @@ function availabilityBadgeColor(status: string): string {
|
||||
return '#64748b'
|
||||
}
|
||||
|
||||
export function PropertyIntelligenceCard({ property: p, onSelect }: Props) {
|
||||
export const PropertyIntelligenceCard = memo(function PropertyIntelligenceCard({ property: p, onSelect }: Props) {
|
||||
const confPct = Math.round(p.confidenceScore * 100)
|
||||
const confColor = p.confidenceScore >= 0.75 ? '#1a7a4a' : p.confidenceScore >= 0.55 ? '#d97706' : '#c0392b'
|
||||
|
||||
@@ -138,4 +139,4 @@ export function PropertyIntelligenceCard({ property: p, onSelect }: Props) {
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useMemo, useCallback } from 'react'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { useReminders } from '../../hooks/useReminders'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
@@ -43,17 +44,21 @@ export function ReminderFeed() {
|
||||
setSearchQuery: s.setSearchQuery,
|
||||
})))
|
||||
|
||||
if (isLoading) return <ReminderSkeleton />
|
||||
|
||||
const reminders = data ?? []
|
||||
const filtered = applyFilters(reminders, filterType, filterStatus, filterPriority, searchQuery)
|
||||
|
||||
function resetFilters() {
|
||||
const filtered = useMemo(
|
||||
() => applyFilters(reminders, filterType, filterStatus, filterPriority, searchQuery),
|
||||
[reminders, filterType, filterStatus, filterPriority, searchQuery],
|
||||
)
|
||||
|
||||
const resetFilters = useCallback(() => {
|
||||
setFilterType('ALL')
|
||||
setFilterStatus('ALL')
|
||||
setFilterPriority('ALL')
|
||||
setSearchQuery('')
|
||||
}
|
||||
}, [setFilterType, setFilterStatus, setFilterPriority, setSearchQuery])
|
||||
|
||||
if (isLoading) return <ReminderSkeleton />
|
||||
|
||||
if (filtered.length === 0) {
|
||||
return <ReminderEmptyState onReset={resetFilters} />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { memo } from 'react'
|
||||
import { Box, Typography, Chip, IconButton, Tooltip } from '@mui/material'
|
||||
import { Check, Bell, X } from 'lucide-react'
|
||||
import type { Reminder } from '../../domain/reminder'
|
||||
@@ -26,7 +27,7 @@ interface Props {
|
||||
reminder: Reminder
|
||||
}
|
||||
|
||||
export function ReminderListRow({ reminder }: Props) {
|
||||
export const ReminderListRow = memo(function ReminderListRow({ reminder }: Props) {
|
||||
const { setSelectedId, setDrawerOpen } = useReminderStore()
|
||||
const complete = useCompleteReminder()
|
||||
const dismiss = useDismissReminder()
|
||||
@@ -139,4 +140,4 @@ export function ReminderListRow({ reminder }: Props) {
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user