Cache visible project table without effect
This commit is contained in:
parent
69a9c5124a
commit
247dc7158b
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in New Issue