React点击按钮后所有Div显示相同信息,如何实现指定Div展示专属信息?
问题分析
你的问题根源在于以下几点:
- 全局状态共享:你用单个
project状态存储选中项目的详情,所有循环渲染的ProjectInfo组件都接收这个状态作为props,导致点击任意按钮后,所有组件都会显示同一个项目的信息。 - Fetch异步逻辑错误:
getProjectInfo中你直接判断res.status,但res是fetch返回的Promise对象,并非HTTP响应,因此这个判断永远不会生效,后续的页面跳转也无法触发。 - 列表Key不规范:使用
index作为列表项的key,当列表项顺序变化时会导致React渲染异常,应该使用唯一标识projectId作为key。
解决方案
针对上述问题,我们可以通过以下步骤修复:
- 维护选中项目状态:用
selectedProject存储当前选中的项目详情,或同时存储selectedProjectId来标识哪个项目需要显示详情。 - 修正Fetch逻辑:在响应的回调中判断HTTP状态,并正确处理异步流程。
- 规范列表Key:使用
projectId作为列表项的唯一key。 - 条件渲染详情组件:仅在当前项目被选中时,才渲染对应的
ProjectInfo组件。
修正后的代码示例
ProjectInfo.js
import React from 'react'; // 修正props命名,更清晰地传递项目信息 const ProjectInfo = ({ project }) => { return ( <div> {project.title} {/* 这里可以添加更多项目详情字段,比如description、projectTypes等 */} </div> ); }; export default ProjectInfo;
ProjectPage.js
import React, { useEffect, useState } from 'react'; import { useKeycloak } from "@react-keycloak/web"; import ProjectInfo from "./ProjectInfo"; import { useNavigate } from "react-router-dom"; const ProjectPage = () => { const { keycloak } = useKeycloak(); const navigate = useNavigate(); const [projectsList, setProjectsList] = useState([ { projectId: "", projectUrl: "", description: '', projectTypes: "", title: '' } ]); // 存储当前选中的项目详情 const [selectedProject, setSelectedProject] = useState(null); const getProjectInfo = (id) => { fetch(`http://localhost:8080/api/v1/user/get-one-project/${id}`, { method: 'GET', headers: { 'Authorization': 'Bearer ' + keycloak.token, 'content-type': 'application/json' }, }) .then(res => { // 先判断响应状态是否成功 if (!res.ok) { throw new Error(`HTTP错误状态: ${res.status}`); } return res.json(); }) .then(response => { setSelectedProject(response); // 若需要跳转到详情页,在这里执行跳转 // navigate("/project-info"); }) .catch(err => console.error('获取项目详情失败:', err)); }; useEffect(() => { fetch(`http://localhost:8080/api/v1/user/users-projects/${keycloak.tokenParsed.sub}`, { method: 'GET', headers: { 'Authorization': 'Bearer ' + keycloak.token, 'content-type': 'application/json' }, }) .then(res => res.json()) .then(data => { setProjectsList(data); console.log(data); }) .catch(err => console.error('获取项目列表失败:', err)); }, [keycloak]); return ( <div> {projectsList.map((p) => ( // 使用projectId作为唯一key <div key={p.projectId} className="max-w-sm rounded overflow-hidden shadow-lg mx-auto"> <div className="px-6 py-4"> <div>{p.projectUrl}</div> <div>{p.title}</div> <div>{p.projectId}</div> <div className="font-bold text-xl mb-2"> <button onClick={() => getProjectInfo(p.projectId)}>info</button> </div> {/* 仅当当前项目被选中时,渲染详情组件 */} {selectedProject?.projectId === p.projectId && ( <div className="text-gray-700 text-base"> <ProjectInfo project={selectedProject} /> </div> )} </div> <div className="px-6 pt-4 pb-2"> <span className="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">#photography</span> <span className="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">#travel</span> <span className="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">#winter</span> </div> </div> ))} </div> ); }; export default ProjectPage;
额外说明
如果你原本的需求是点击按钮后跳转到独立的详情页(/project-info),那么不需要在当前页面渲染ProjectInfo,而是在详情页中通过路由参数获取projectId,再调用接口获取详情进行展示。这种方式更符合单页应用的路由设计逻辑。
内容的提问来源于stack exchange,提问作者nissiis
相关产品推荐
相关产品推荐

