Fix project detail crash: replace dynamic hooks with single query
Build and Push Docker Image / build (push) Failing after 8s
Details
Build and Push Docker Image / build (push) Failing after 8s
Details
The project detail page called useQuery inside .map() to fetch file requirements per round, violating React's rules of hooks. When competitionRounds changed from [] to [round1, round2], the hook count changed, causing React to crash with "Cannot read properties of undefined (reading 'length')". Fix: Add listRequirementsByRounds endpoint that accepts multiple roundIds in one query, replacing the dynamic hook pattern with a single stable useQuery call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
65a22e6f19
commit
f12c29103c
|
|
@ -105,14 +105,13 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||||
// Extract all rounds from the competition
|
// Extract all rounds from the competition
|
||||||
const competitionRounds = competition?.rounds || []
|
const competitionRounds = competition?.rounds || []
|
||||||
|
|
||||||
// Fetch requirements for each round
|
// Fetch requirements for all rounds in a single query (avoids dynamic hook violation)
|
||||||
const requirementQueries = competitionRounds.map((round: { id: string; name: string }) =>
|
const roundIds = competitionRounds.map((r: { id: string }) => r.id)
|
||||||
trpc.file.listRequirements.useQuery({ roundId: round.id })
|
const { data: allRequirements = [] } = trpc.file.listRequirementsByRounds.useQuery(
|
||||||
|
{ roundIds },
|
||||||
|
{ enabled: roundIds.length > 0 }
|
||||||
)
|
)
|
||||||
|
|
||||||
// Combine requirements from all rounds
|
|
||||||
const allRequirements = requirementQueries.flatMap((q: { data?: unknown[] }) => q.data || [])
|
|
||||||
|
|
||||||
const utils = trpc.useUtils()
|
const utils = trpc.useUtils()
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|
@ -592,7 +591,7 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center gap-2 text-xs text-muted-foreground mt-0.5">
|
<div className="flex items-center gap-2 text-xs text-muted-foreground mt-0.5">
|
||||||
{req.acceptedMimeTypes.length > 0 && (
|
{req.acceptedMimeTypes?.length > 0 && (
|
||||||
<span>
|
<span>
|
||||||
{req.acceptedMimeTypes.map((mime: string) => {
|
{req.acceptedMimeTypes.map((mime: string) => {
|
||||||
if (mime === 'application/pdf') return 'PDF'
|
if (mime === 'application/pdf') return 'PDF'
|
||||||
|
|
|
||||||
|
|
@ -818,6 +818,20 @@ export const fileRouter = router({
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List file requirements for multiple rounds in a single query.
|
||||||
|
* Avoids dynamic hook violations when fetching requirements per-round.
|
||||||
|
*/
|
||||||
|
listRequirementsByRounds: protectedProcedure
|
||||||
|
.input(z.object({ roundIds: z.array(z.string()).max(50) }))
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
if (input.roundIds.length === 0) return []
|
||||||
|
return ctx.prisma.fileRequirement.findMany({
|
||||||
|
where: { roundId: { in: input.roundIds } },
|
||||||
|
orderBy: { sortOrder: 'asc' },
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a file requirement for a stage (admin only)
|
* Create a file requirement for a stage (admin only)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue