Cache visible project table without effect

This commit is contained in:
Neil 2026-05-30 12:49:26 -07:00 committed by GitHub
parent 69a9c5124a
commit 247dc7158b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 137 additions and 21 deletions

View File

@ -48,6 +48,11 @@ import ProjectPicker, { type ResolvedProjectSelection } from './ProjectPicker'
import ProjectViewList from './ProjectViewList'
import ProjectItemSlugDialog from './ProjectItemSlugDialog'
import { filterProjectTableRowsByOpenRepos } from './project-row-filtering'
import {
getNextVisibleProjectTableCache,
getVisibleProjectTable,
type CachedVisibleProjectTable
} from './project-visible-table-cache'
type Props = Record<string, never>
@ -308,27 +313,23 @@ export default function ProjectViewWrapper(_props: Props = {} as Props): React.J
() => (table && slugIndexReady ? filterProjectTableRowsByOpenRepos(table, lookupSlug) : null),
[table, slugIndexReady, lookupSlug]
)
const [lastFilteredTable, setLastFilteredTable] = useState<{
cacheKey: string
table: GitHubProjectTable
} | null>(null)
useEffect(() => {
if (!currentCacheKey || !table) {
setLastFilteredTable(null)
return
}
if (slugIndexReady && filteredTable) {
setLastFilteredTable({ cacheKey: currentCacheKey, table: filteredTable })
}
}, [currentCacheKey, table, slugIndexReady, filteredTable])
const visibleTable =
slugIndexReady || !currentCacheKey
? filteredTable
: lastFilteredTable?.cacheKey === currentCacheKey
? lastFilteredTable.table
: null
const lastFilteredTableRef = useRef<CachedVisibleProjectTable | null>(null)
// Why: this cache only prevents a blank table while the repo slug index
// rebuilds; a ref preserves the previous render value without scheduling
// a second render after every filtered-table change.
lastFilteredTableRef.current = getNextVisibleProjectTableCache({
currentCacheKey,
sourceTable: table,
slugIndexReady,
filteredTable,
previous: lastFilteredTableRef.current
})
const visibleTable = getVisibleProjectTable({
currentCacheKey,
slugIndexReady,
filteredTable,
cachedTable: lastFilteredTableRef.current
})
// Parent-dropped toast, once per table.
useEffect(() => {

View File

@ -0,0 +1,81 @@
import { describe, expect, it } from 'vitest'
import type { GitHubProjectTable } from '../../../../shared/github-project-types'
import {
getNextVisibleProjectTableCache,
getVisibleProjectTable
} from './project-visible-table-cache'
function table(id: string): GitHubProjectTable {
return { id } as unknown as GitHubProjectTable
}
describe('project visible table cache', () => {
it('stores the filtered table while the slug index is ready', () => {
const sourceTable = table('source')
const filteredTable = table('filtered')
expect(
getNextVisibleProjectTableCache({
currentCacheKey: 'project:view',
sourceTable,
slugIndexReady: true,
filteredTable,
previous: null
})
).toEqual({ cacheKey: 'project:view', table: filteredTable })
})
it('keeps the previous cache while the slug index is rebuilding', () => {
const previous = { cacheKey: 'project:view', table: table('previous') }
expect(
getNextVisibleProjectTableCache({
currentCacheKey: 'project:view',
sourceTable: table('source'),
slugIndexReady: false,
filteredTable: null,
previous
})
).toBe(previous)
})
it('drops the cache when there is no current table', () => {
const previous = { cacheKey: 'project:view', table: table('previous') }
expect(
getNextVisibleProjectTableCache({
currentCacheKey: null,
sourceTable: null,
slugIndexReady: false,
filteredTable: null,
previous
})
).toBeNull()
})
it('shows a matching cached table while the slug index is rebuilding', () => {
const cachedTable = { cacheKey: 'project:view', table: table('cached') }
expect(
getVisibleProjectTable({
currentCacheKey: 'project:view',
slugIndexReady: false,
filteredTable: null,
cachedTable
})
).toBe(cachedTable.table)
})
it('does not show stale cached data for a different cache key', () => {
const cachedTable = { cacheKey: 'other:view', table: table('cached') }
expect(
getVisibleProjectTable({
currentCacheKey: 'project:view',
slugIndexReady: false,
filteredTable: null,
cachedTable
})
).toBeNull()
})
})

View File

@ -0,0 +1,34 @@
import type { GitHubProjectTable } from '../../../../shared/github-project-types'
export type CachedVisibleProjectTable = {
cacheKey: string
table: GitHubProjectTable
}
export function getNextVisibleProjectTableCache(input: {
currentCacheKey: string | null
sourceTable: GitHubProjectTable | null
slugIndexReady: boolean
filteredTable: GitHubProjectTable | null
previous: CachedVisibleProjectTable | null
}): CachedVisibleProjectTable | null {
if (!input.currentCacheKey || !input.sourceTable) {
return null
}
if (input.slugIndexReady && input.filteredTable) {
return { cacheKey: input.currentCacheKey, table: input.filteredTable }
}
return input.previous
}
export function getVisibleProjectTable(input: {
currentCacheKey: string | null
slugIndexReady: boolean
filteredTable: GitHubProjectTable | null
cachedTable: CachedVisibleProjectTable | null
}): GitHubProjectTable | null {
if (input.slugIndexReady || !input.currentCacheKey) {
return input.filteredTable
}
return input.cachedTable?.cacheKey === input.currentCacheKey ? input.cachedTable.table : null
}