如何根据下拉选中等级展示对应Descendant数据(Next.js+Prisma)
问题描述
我正在创建一个组件,希望当下拉菜单选中某个等级选项时,仅展示该等级对应的属性数据。目前组件会显示全部40个等级的属性,但我需要实现选中1级就只展示1级数据,以此类推。我刚接触Prisma,不清楚如何传递选中的等级值来过滤展示对应数据。
组件代码
import prisma from '@/lib/db'; import Link from '../../../../node_modules/next/link'; export default async function SingleDescendant({ params }) { const descendants = await prisma.descendants.findUnique({ where: { slug: params.slug, }, include: { skills: true, levels: { include: { stats: true, } }, } }); return ( <> <main className="flex min-h-screen flex-col p-24"> <Link href={`/descendants`}>返回</Link> {[descendants].map((d) => ( <div key={d.id}> <div className="title text-center"> <h1>{d.descendant_name}</h1> </div> <div className="descendant-details"> <div className="flex flex-row"> <img src={d.descendant_img} alt="" /> <div className='flex flex-row flex-wrap gap-10'> <select id="level" className='levels bg-gray-500 text-white h-fit text-center'> <option selected disabled>选择等级</option> {descendants.levels.map((level) => ( <> <option value={level.level}>{level.level}</option> </> ))} </select> {descendants.levels.map((level) => ( <div className='stats flex flex-col'> {level.stats.map((d) => ( <> <p>最大生命值:{d.hp_value}</p> <p>最大护盾值:{d.shield_value}</p> <p>最大魔法值:{d.mp_value}</p> <p>防御:{d.def_value}</p> <p>脱战护盾回复:{d.out_of_combat_value}</p> <p>战斗中护盾回复:{d.in_combat_value}</p> </> ))} </div> ))} </div> </div> <div className="flex flex-row gap-4"> {descendants.skills.map((skill) => ( <> <div key={skill.id} className='border shadow-md w-1/4 p-4'> <p>{skill.skill_type}</p> <p>{skill.skill_name}</p> <p>{skill.element_type}</p> <p>{skill.arche_type}</p> <p>{skill.skill_description}</p> <div className="w-full flex justify-center "> <img src={skill.skill_image} alt="" /> </div> </div> </> ))} </div> </div> </div> ))} </main> </> ) }
Prisma Schema
generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model Descendants { id String @id @default(cuid()) descendant_name String @unique slug String @unique descendant_img String skills DescendantSkills[] levels DescendantLevels[] @@index([slug]) } model DescendantSkills { id String @id @default(cuid()) skill_type String skill_name String element_type String arche_type String skill_image String skill_description String Descendants Descendants? @relation(fields: [descendantsId], references: [id]) descendantsId String? } model DescendantLevels { id String @id @default(cuid()) level Int stats DescendantStats[] Descendants Descendants? @relation(fields: [descendantsId], references: [id]) descendantsId String? } model DescendantStats { id String @id @default(cuid()) max_hp String hp_value Int max_shield String shield_value Int max_mp String mp_value Int def String def_value Int shield_recovery_out_of_combat String out_of_combat_value Int shield_recovery_in_combat String in_combat_value Int DescendantLevels DescendantLevels? @relation(fields: [descendantLevelsId], references: [id]) descendantLevelsId String? }
解决方案
你的问题核心是客户端交互状态管理,和Prisma本身无关——你已经通过Prisma把所有等级数据拉取到前端了,只需要在前端根据选中的等级过滤展示即可。具体修改步骤如下:
- 转为客户端组件:添加
'use client'指令,引入React的useState钩子处理交互状态。 - 存储选中等级:用
useState保存当前选中的等级值。 - 处理下拉选择事件:给
select元素添加onChange事件,更新选中状态。 - 过滤渲染数据:根据选中的等级,筛选对应的数据并渲染。
修改后的完整组件代码
'use client'; import prisma from '@/lib/db'; import Link from 'next/link'; import { useState } from 'react'; export default async function SingleDescendant({ params }) { const descendants = await prisma.descendants.findUnique({ where: { slug: params.slug, }, include: { skills: true, levels: { include: { stats: true, } }, } }); // 初始化选中等级状态,默认设为null(未选择状态) const [selectedLevel, setSelectedLevel] = useState(null); return ( <> <main className="flex min-h-screen flex-col p-24"> <Link href={`/descendants`}>返回</Link> <div key={descendants.id}> <div className="title text-center"> <h1>{descendants.descendant_name}</h1> </div> <div className="descendant-details"> <div className="flex flex-row"> <img src={descendants.descendant_img} alt="" /> <div className='flex flex-row flex-wrap gap-10'> <select id="level" className='levels bg-gray-500 text-white h-fit text-center' onChange={(e) => setSelectedLevel(parseInt(e.target.value))} > <option selected disabled>选择等级</option> {descendants.levels.map((level) => ( <option key={level.id} value={level.level}>{level.level}</option> ))} </select> {/* 仅渲染选中等级的属性数据 */} {selectedLevel && ( descendants.levels .filter(level => level.level === selectedLevel) .map((level) => ( <div key={level.id} className='stats flex flex-col'> {level.stats.map((stat) => ( <div key={stat.id}> <p>最大生命值:{stat.hp_value}</p> <p>最大护盾值:{stat.shield_value}</p> <p>最大魔法值:{stat.mp_value}</p> <p>防御:{stat.def_value}</p> <p>脱战护盾回复:{stat.out_of_combat_value}</p> <p>战斗中护盾回复:{stat.in_combat_value}</p> </div> ))} </div> )) )} </div> </div> <div className="flex flex-row gap-4"> {descendants.skills.map((skill) => ( <div key={skill.id} className='border shadow-md w-1/4 p-4'> <p>{skill.skill_type}</p> <p>{skill.skill_name}</p> <p>{skill.element_type}</p> <p>{skill.arche_type}</p> <p>{skill.skill_description}</p> <div className="w-full flex justify-center "> <img src={skill.skill_image} alt="" /> </div> </div> ))} </div> </div> </div> </main> </> ) }
关键修改说明
- 客户端组件标识:
'use client'让Next.js启用客户端交互能力。 - 状态管理:
selectedLevel存储选中的等级数字,确保和Prisma返回的level.level(Int类型)匹配。 - 选择事件处理:
onChange把下拉框的字符串值转为数字,避免类型不匹配导致过滤失败。 - 条件渲染:只有选中等级后才渲染对应属性,初始状态下不显示任何等级数据。
- 消除React警告:给所有循环渲染的元素添加唯一
key属性。
内容的提问来源于stack exchange,提问作者S. Lowe
相关产品推荐
相关产品推荐

