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
|
||||
const competitionRounds = competition?.rounds || []
|
||||
|
||||
// Fetch requirements for each round
|
||||
const requirementQueries = competitionRounds.map((round: { id: string; name: string }) =>
|
||||
trpc.file.listRequirements.useQuery({ roundId: round.id })
|
||||
// Fetch requirements for all rounds in a single query (avoids dynamic hook violation)
|
||||
const roundIds = competitionRounds.map((r: { id: string }) => r.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()
|
||||
|
||||
if (isLoading) {
|
||||
|
|
@ -592,7 +591,7 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
|||
</p>
|
||||
)}
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground mt-0.5">
|
||||
{req.acceptedMimeTypes.length > 0 && (
|
||||
{req.acceptedMimeTypes?.length > 0 && (
|
||||
<span>
|
||||
{req.acceptedMimeTypes.map((mime: string) => {
|
||||
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)
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue