T3栈饮酒游戏应用中卡牌随机加载后不定期停止渲染问题求助
T3栈饮酒游戏应用中卡牌随机加载后不定期停止渲染问题求助
大家好,我是一名初级开发者,最近基于T3 Create App + TypeScript + Drizzle + PostgreSQL开发了一款“真心话大冒险”类的饮酒游戏。
目前遇到一个非常诡异的问题:应用可以正常获取卡牌数据,但会不定期停止渲染新卡牌——有时候抽1-2张就卡住,有时候能连续抽到8张,之后就完全无法加载新卡牌了,必须刷新页面才能恢复正常。
更让人困惑的是:
- 控制台没有任何错误日志输出
- 数据库查询的控制台日志显示执行成功
- 但界面就是不会渲染新的卡牌
下面是我的相关核心代码:
1. Game页面组件代码
'use client' import '@/styles/gameStyle.css' import React, { useState, useEffect, useCallback } from "react" import { api } from "@/trpc/react" import { v4 as uuidv4 } from 'uuid' import { useMotionValue, useDragControls, motion, AnimatePresence } from "framer-motion" import Card from "../components/card/card" import DialogInfoModal from "../components/game/dialogInfoModal" import PlayButton from "../components/game/PlayButton" import PlayerForm from "../components/game/PlayerForm" import DeckChoose from "../components/game/DeckChoose" import { Button } from "@/components/ui/button" import { useToast } from "@/hooks/use-toast" import { Loader2 } from "lucide-react" export default function Game() { const [selectedDeckIds, setSelectedDeckIds] = useState<string[]>([]) const [gameStage, setGameStage] = useState<'initial' | 'deckChoose' | 'playerForm' | 'gamePlay'>('initial') const [currentMatchId, setCurrentMatchId] = useState<string | null>(null) const [playersList, setPlayersList] = useState<{ id: string; name: string }[]>([]) const [currentPlayerIndex, setCurrentPlayerIndex] = useState(0) const [exitX, setExitX] = useState(0) const x = useMotionValue(0) const dragControls = useDragControls() const { toast } = useToast() const { data: cardData, refetch, isLoading: isCardLoading, error: cardError } = api.card.getRandomCardByDeckId.useQuery( { deckIds: selectedDeckIds }, { enabled: selectedDeckIds.length > 0 && gameStage === 'gamePlay', retry: false, onError: (error) => { toast({ description: 'Error fetching card: ' + error.message, variant: "destructive" }) } } ) const { mutate: playerMutate } = api.players.create.useMutation() const { mutate: matchesMutate, isLoading: isMatchMutating } = api.match.create.useMutation({ onSuccess: (matchData) => { setCurrentMatchId(matchData.id) console.log('Match created with ID:', matchData.id) }, onError: (error) => { toast({ description: 'Erro ao criar partida: ' + error.message, variant: "destructive" }) } }) const { mutate: matchPlayersMutate } = api.matchPlayers.create.useMutation() useEffect(() => { if (gameStage === 'gamePlay' && selectedDeckIds.length > 0) { refetch() } }, [gameStage, selectedDeckIds, refetch]) const handlePlayButtonClick = () => { setGameStage('deckChoose') createMatch() } const handlePlayerForm = () => { setGameStage('gamePlay') } const createMatch = () => { const date = new Date() const status = 'Started' matchesMutate({ status, date }) } const handlePlayerSubmit = (name: string) => { const id = uuidv4() playerMutate({ id, name }, { onSuccess: () => { toast({ description: 'Jogador Adicionado com Sucesso!' }) setPlayersList((prev) => [...prev, { id, name }]) if (currentMatchId) { matchPlayersMutate({ matchId: currentMatchId, playerId: id, }) } } }) } const handleDeckSelection = useCallback((chosenDeckIds: string[]) => { setSelectedDeckIds(chosenDeckIds) setGameStage('playerForm') }, []) const handleDragEnd = ( _: MouseEvent | TouchEvent | PointerEvent, info: { offset: { x: number } } ) => { if (info.offset.x < -150 || info.offset.x > 150) { setExitX(info.offset.x < -150 ? -400 : 400) console.log(info.offset.x < -150 ? 'esquerda' : 'direita') setCurrentPlayerIndex((prevIndex) => (prevIndex + 1) % playersList.length) refetch() } } return ( <main className="relative main flex h-screen flex-col items-center justify-center bg-gradient-to-b from-[#15042e] to-[#070012]"> <AnimatePresence mode="wait"> {gameStage === 'initial' && ( <motion.div key="playButton" initial={{ opacity: 0, scale: 0 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0 }} transition={{ duration: 0.5 }} > <PlayButton onClick={handlePlayButtonClick} /> </motion.div> )} {gameStage === 'deckChoose' && ( <motion.div key="deckChoose" initial={{ opacity: 0, scale: 0 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0 }} transition={{ duration: 0.5 }} > <DeckChoose onDeckSelection={handleDeckSelection} /> </motion.div> )} {gameStage === 'playerForm' && ( <motion.div key="playerForm" initial={{ opacity: 0, scale: 0 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0 }} transition={{ duration: 0.5 }} > <PlayerForm setPlayerFormVisible={handlePlayerForm} onSubmit={handlePlayerSubmit} playersList={playersList} /> </motion.div> )} {gameStage === 'gamePlay' && ( <motion.div key="gamePlay" initial={{ opacity: 0, scale: 0 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0 }} transition={{ duration: 0.5 }} className="w-full h-full flex flex-col items-center justify-center" > <h2 className="text-white mb-4 absolute top-20 text-xl font-semibold">Turno de: {playersList[currentPlayerIndex]?.name}</h2> <div className="flex justify-center items-center w-full h-full"> {isCardLoading ? ( <Loader2 className="h-8 w-8 animate-spin" /> ) : cardError ? ( <div className="text-center"> <p className="text-red-500 mb-4">Error: {cardError.message}</p> <Button onClick={() => refetch()}>Tente Novamente</Button> </div> ) : cardData && cardData.length > 0 ? ( <AnimatePresence> {cardData.map((data) => ( <Card key={data.id ?? uuidv4()} drag="x" cardDescription={data.description ?? ''} cardNumber={data.level ?? 0} cardTheme={data.theme ?? ''} cardName={data.name ?? ''} cardSubtitle={data.subtitle ?? ''} x={x} dragControls={dragControls} onDragEnd={handleDragEnd} /> ))} </AnimatePresence> ) : ( <p className="text-white">Sem Cartas Nesse Deck</p> )} </div> </motion.div> )} </AnimatePresence> <div className="absolute bottom-8 right-8"> <DialogInfoModal /> </div> </main> ) }
2. Card Router(TRPC后端路由)代码
import { z } from "zod"; import { createTRPCRouter, publicProcedure } from "@/server/api/trpc"; import { cards } from "@/server/db/schema"; import { db } from "@/server/db/index" import { sql, inArray } from "drizzle-orm"; export async function getRandomCardByDeckIds(deckIds: string[]) { if (deckIds.length === 0) { throw new Error("No deck IDs provided"); } const query = sql` SELECT * FROM "pinga-party_card" WHERE ${inArray(cards.deckId, deckIds)} ORDER BY random() LIMIT 1 `; return await db.execute(query); } export async function getRandomCard() { return await db.execute(sql`SELECT * FROM "pinga-party_card" ORDER BY random() LIMIT 1`); } export const cardRouter = createTRPCRouter({ getRandomCardByDeckId: publicProcedure .input(z.object({ deckIds: z.array(z.string()) })) .query(async ({ input }) => { return await getRandomCardByDeckIds(input.deckIds); }), getRandomCard: publicProcedure.query(async () => { return await getRandomCard(); }), get: publicProcedure.query(async () => { return await db.query.cards.findMany({ with: { deck: true, }, }); }), create: publicProcedure .input( z.object({ name: z.string(), description: z.string(), theme: z.string(), level: z.number().int(), deckId: z.string(), subtitle: z.string(), }) ) .mutation(async (opts) => { const { input } = opts; await db.insert(cards).values({ name: input.name, description: input.description, theme: input.theme, level: input.level, deckId: input.deckId, subtitle: input.subtitle, }) }), })
我已经检查过数据库中的卡牌数据完整性,也尝试过添加调试日志、用AI工具分析代码,但完全找不到问题根源。有没有大佬能帮我分析下,可能是哪里的逻辑导致了这个问题?非常感谢!
备注:内容来源于stack exchange,提问作者Pedro Paitax
相关产品推荐
相关产品推荐

