
本教程将解决reactjs开发中常见的列表项点击问题,即点击一个元素时所有同类元素同时展开详情。通过引入“状态提升”模式,我们将演示如何在父组件中管理当前选中项的id,并将其作为props传递给子组件进行条件渲染,从而实现只有被点击的职位详情才精确显示,提升用户体验和应用性能。
在构建交互式用户界面时,我们经常会遇到这样的场景:页面上展示一个列表,用户点击其中一个列表项时,该项会展开显示更多详细信息。然而,一个常见的问题是,当点击某个列表项后,所有同类型的列表项都同时展开了,这显然不是我们期望的行为。本教程将深入探讨这一问题,并提供一个基于ReactJS的健壮解决方案,确保只有被点击的列表项才显示其专属详情。
在提供的代码示例中,问题出在 App.js 和 JobBoardComponent.js 之间状态管理的逻辑。App.js 中定义了一个名为 toggled 的状态变量,并通过 props 将其传递给所有的 JobBoardComponent 实例。
// App.js 核心片段
function App() {
const [toggled, setToggled] = useState(true); // 这个状态是全局的
return (
<div>
{/* ...其他组件 */}
<div>
{ activeCategory == 0 ? jobs.map((job) => <JobBoardComponent toggled={toggled} setToggled={setToggled} job={job} key={job.id} />)
: filtered.map((job) => <JobBoardComponent toggled={toggled} setToggled={setToggled}
job={job} key={job.id} />)}
</div>
</div>
);
}在 JobBoardComponent.js 中,handleClick 函数会修改这个从父组件传递下来的 toggled 状态:
// JobBoardComponent.js 核心片段
function JobBoardComponent({ toggled, setToggled, job }) {
const handleClick = event => {
setToggled(!toggled); // 修改的是App.js中的toggled状态
};
return (
<div id="jobboard" onClick={handleClick} className={`...`}>
{/* ...职位摘要信息 */}
<div id="jobdesc" className={toggled ? "hidden font-mono my-10 " : "font-mono my-10"} >
{/* ...详细信息,其可见性由toggled控制 */}
</div>
{/* ...其他信息 */}
</div>
)
}由于 toggled 状态是在 App.js 中声明的,并且被所有 JobBoardComponent 实例共享,当任何一个 JobBoardComponent 调用 setToggled(!toggled) 时,它实际上改变的是所有组件共同依赖的那个 toggled 状态。这会导致 App.js 重新渲染,并将新的 toggled 值传递给所有子组件,从而使得所有 JobBoardComponent 的详细信息同时显示或隐藏。
要解决这个问题,我们需要让每个 JobBoardComponent 实例能够独立地控制其详情的显示状态。最直接且符合React哲学的方法是使用“状态提升”(Lifting State Up)模式。这意味着我们将管理哪个职位详情应该展开的状态提升到它们的共同父组件(App.js),但不是一个简单的布尔值,而是一个能够唯一标识被点击项的值,例如其 ID。
首先,在 App.js 中引入一个新的状态变量,用于存储当前被选中(即需要显示详情)的职位 ID。
// App.js
import './App.css';
import React, {useState, useEffect} from 'react'
import jobData from "./assets/data.json"
import JobBoardComponent from './components/JobBoardComponent';
// ... 其他导入
function App() {
const [jobs, setJobs] = useState([])
// ... 其他状态
// 新增状态:存储当前被选中职位ID
const [selectedJobId, setSelectedJobId] = useState(null); // 初始化为null,表示没有职位被选中
useEffect(() => {
setJobs(jobData)
}, [])
// ... 其他逻辑
return (
<div className="App w-auto">
{/* ... NavBar, BannerPage, EmailBar, Filters */}
<div>
{ (activeCategory === 0 ? jobs : filtered).map((job) => (
<JobBoardComponent
job={job}
key={job.id}
// 传递一个回调函数,用于更新父组件的selectedJobId状态
onJobSelect={setSelectedJobId}
// 传递一个布尔值,指示当前job是否是选中项
isSelected={selectedJobId === job.id}
/>
))}
</div>
</div>
);
}
export default App;在这里,我们做了以下改动:
接下来,修改 JobBoardComponent 以接收新的 onJobSelect 和 isSelected prop,并根据 isSelected prop 来决定是否显示详细信息。
// JobBoardComponent.js
import React from "react" // 移除了useState和useEffect,因为toggled状态不再由子组件自身管理
function JobBoardComponent({ onJobSelect, isSelected, job }) { // 接收新的props
const langAndTools = []
const skill = job.skills
const tools = job.tools
if (skill && tools) {
langAndTools.push(...skill)
langAndTools.push(...tools)
}
// 移除了useEffect和本地的toggled状态
const handleClick = () => {
// 当点击时,调用父组件传递下来的onJobSelect函数,并传入当前job的ID
// 如果当前job已经是选中状态,再次点击则关闭详情(将selectedJobId设为null)
// 否则,设置为当前job的ID
onJobSelect(isSelected ? null : job.id);
};
return (
// 注意:onClick现在直接调用handleClick,并且不需要传递event
<div id="jobboard" onClick={handleClick} className={`flex flex-col hover:bg-gray-100 bg-white shadow-md m-4 mb-20 px-6 -mt-10 rounded ${job.featured && 'border-l-4 border-black'}`}>
<div>
<img className="-mt-10 mb-4 w-20 h-20"src={job.logo} alt={job.company}></img>
</div>
<div className="text-left mt-1">
<h3 className="font-mono font-bold text-blue-600">
{job.company}
{job.fresh && (<span className="text-blue-100 bg-blue-500 font-bold m-2 py-1 px-2 rounded-full">
New</span>)}
{job.featured && (<span className="text-white bg-gray-800 font-bold m-2 py-1 px-2 rounded-full">Featured</span>)}
</h3>
<h2 className="font-mono font-bold text-xl my-2">{job.position}</h2>
<p className="font-mono text-gray-700">
{job.postedAt} · {job.contract} · {job.location}
</p>
</div>
{/* 根据isSelected prop来条件渲染详细信息 */}
{isSelected && (
<div id="jobdesc" className="font-mono my-10" >
<h3>Who We Are:</h3>
<p>{job.whoweare}</p>
<br></br>
<h3>Job Description:</h3>
<p>{job.description}</p>
<br></br>
<br></br>
<a id="applybtn" target="_blank" href={job.joblink} className="font-mono text-white bg-gradient-to-r from-blue-500 via-blue-600 to-blue-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800 font-medium rounded-lg text-sm px-32 py-2 text-center mr-2 content-center"> Apply </a>
<br></br>
</div>
)}
<div className="flex flex-wrap items-center mt-4 mr-4 pt-4 border-t-2 border-gray-300 border-solid">
{langAndTools ? (
langAndTools.map((langAndTool) => (
<span className="text-blue-600 bg-blue-100 font-bold mr-4 mb-4 p-2 rounded">{langAndTool}</span>
))
)
: " "
}
</div>
</div>
)
}
export default JobBoardComponent在 JobBoardComponent 中,我们进行了以下关键修改:
通过上述修改,当用户点击某个 JobBoardComponent 时:
这种模式在React中被称为“状态提升”,是管理组件之间共享状态和交互行为的常用且推荐的方法。它使得父组件能够集中控制子组件的行为,同时保持子组件的纯净和可复用性。
注意事项:
通过采用状态提升和精确的条件渲染,我们成功解决了列表项全局展开的问题,为用户提供了更直观和高效的交互体验。这是React开发中一个基础而重要的模式,掌握它对于构建复杂的、响应式的用户界面至关重要。
以上就是ReactJS中实现精确点击显示:避免列表项全局展开的教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号