为什么我的卡片有时会停止渲染?

问题描述 投票:0回答:1

我是一名初级开发人员,我创建了一个饮酒游戏,一种“敢不敢喝”的游戏。我使用了带有 TypeScript、Drizzle 和 PostgreSQL 的 T3 Create 应用程序。

问题是我的应用程序获取一个值和卡片,但随后停止渲染它们。有时它在一张或两张卡后停止,有时它会增加到八张,然后它就停止渲染。我必须刷新网站才能再次工作。

我没有收到任何错误日志或任何东西;控制台甚至显示查询已成功执行,但它就是不起作用。

'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>
  )
}

这就是游戏页面组件。

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,
            })
        }),
})

这就是模型。

我已经检查了数据库,但问题似乎可能出在代码中的某个地方。

需要什么我就发给你。我已经尝试过日志、数据库、gpts(如 chatgpt、gemini、v0),但没有成功。

reactjs typescript postgresql next.js drizzle
1个回答
0
投票

很高兴认识你。我已阅读您的问题并检查您的代码。 我希望我的解决方案可以帮助您。

当然!以下是您的 React.js 和 Next.js 应用程序可能获取数据但无法渲染数据的一些潜在原因,以及故障排除步骤:

  1. 状态管理问题:确保在获取数据后正确更新组件的状态。如果状态未正确更新,即使数据已成功获取,组件也不会重新渲染。

  2. 条件渲染逻辑:检查渲染数据的条件。确保您的渲染逻辑正确处理数据尚不可用或正在加载的场景。

  3. 处理异步获取:确保您的获取逻辑正确处理承诺,并且您可以很好地管理加载状态。正确的错误处理还可以提供对问题的洞察。

  4. 服务器端和静态属性:如果使用

    getServerSideProps
    getStaticProps
    等 Next.js 功能,请验证这些函数是否将正确的数据返回到您的组件。

  5. 渲染列表:渲染数据列表时,确认每个项目都有唯一的键。这有助于 React 跟踪列表的更改并可能影响渲染。

  6. 上下文或状态管理工具问题:如果使用上下文或状态管理库,请确保组件正确订阅更新。

如果检查这些区域后问题仍然存在,请考虑隔离应用程序的某些部分以确定问题的根源。 如果你愿意,我可以在短时间内解决你的问题。

© www.soinside.com 2019 - 2024. All rights reserved.