React组件开发:如何点击编辑仅打开对应科目的输入框?
解决React中编辑按钮仅展开对应科目输入框的问题
问题分析
当前代码使用全局布尔值open控制所有输入框的显示/隐藏,点击任意编辑按钮都会将open设为true,导致所有输入框同时展开。要实现仅展开对应科目的输入框,需要跟踪当前正在编辑的科目ID,而非使用全局状态。
解决方案步骤
- 替换全局状态:将
open改为记录当前编辑的科目ID,同时新增逻辑初始化编辑输入框的原有数据 - 绑定编辑按钮事件:点击时记录目标科目ID,并将该科目数据填充到输入框
- 条件渲染输入框:仅当当前科目ID与编辑状态中的ID匹配时,显示对应输入框
- 优化更新请求:使用PUT请求更新指定科目,成功后重置编辑状态并重新拉取数据,避免页面刷新
修改后的完整代码
import React, { useEffect, useState } from 'react' import Navbar from '../../components/Navbar' import './Exam.sass' import officeSupplies from '../../assets/office supplies-6010.svg' import pencilToSquare from '../../assets/pencil-to-square.svg' import trash from '../../assets/trash.svg' import { useNavigate, useParams } from 'react-router-dom' import axios from 'axios' const Exam = () => { const token = localStorage.getItem('token') const [subjectName, setSubjectName] = useState(''); const [achievableScore, setAchievableScore] = useState(0); const [obtainedScore, setObtainedScore] = useState(0); const [title, setTitle] = useState(''); const [subjectData, setSubjectData] = useState([]); const [isLoading, setIsLoading] = useState(false); // 替换原open状态,用科目ID跟踪当前编辑项 const [editingSubjectId, setEditingSubjectId] = useState(null); const { exam } = useParams() const navigate = useNavigate() const verify = () => { if (!token) navigate('/login') } // 新增科目更新请求函数 const requestUpdateSubject = async (e, subjectId) => { e.preventDefault() try { const floatAchievableScore = parseFloat(achievableScore) const floatObtainedScore = parseFloat(obtainedScore) await axios.put(`http://localhost:5000/exams/${exam}/subjects/${subjectId}`, { subjectName, achievableScore: floatAchievableScore, obtainedScore: floatObtainedScore }) // 更新成功后重置编辑状态,重新拉取数据 setEditingSubjectId(null) requestSubjectData() } catch (err) { console.log(err); } } const requestAddSubject = async e => { e.preventDefault() try { const floatAchievableScore = parseFloat(achievableScore) const floatObtainedScore = parseFloat(obtainedScore) await axios.post(`http://localhost:5000/exams/${exam}/addsubject`, { subjectName, achievableScore: floatAchievableScore, obtainedScore: floatObtainedScore }) // 新增后清空输入框 setSubjectName('') setAchievableScore(0) setObtainedScore(0) requestSubjectData() } catch (err) { console.log(err); } } const requestSubjectData = async () => { setIsLoading(true) try { axios.defaults.headers.common['Authorization'] = localStorage.getItem('token') const res = await axios.get(`http://localhost:5000/exams/${exam}`) setSubjectData(res.data.subjects) setTitle(res.data.examName) } catch (err) { console.log(err); } setIsLoading(false) } useEffect(() => { verify() }, []) useEffect(() => { requestSubjectData() }, []) // 点击编辑按钮时填充当前科目数据到输入框 const handleEditClick = (subject) => { setEditingSubjectId(subject._id) setSubjectName(subject.subjectName) setAchievableScore(subject.achievableScore) setObtainedScore(subject.obtainedScore) } return ( <> <div className="exam"> <Navbar suffix={`Dashboard > ${title}`} /> <div className="exam__title"> <h1 className="exam__title-text">{title}</h1> </div> {!isLoading && subjectData.length == 0 && <> <div className="exam__image"> <img src={officeSupplies} alt="" className="exam__image-img" /> </div> <div className="exam__empty"> <h1 className="exam__empty-title">No Subjects added yet!</h1> </div> </> } {!isLoading && subjectData && subjectData.length > 0 && subjectData.map(subject => <React.Fragment key={subject._id}> <div className="dashboard__subject__data"> <h3 className="dashboard__subject__name">{subject.subjectName}</h3> <h3 className="dashboard__subject__percent">{subject.percentage}%</h3> <h3 className="dashboard__subject__total">{`${subject.obtainedScore}/${subject.achievableScore}`}</h3> <div className="dashboard__subject-options"> <button onClick={() => handleEditClick(subject)} className="dashboard__subject-option btn"> <img src={pencilToSquare} alt="" className="dashboard__subject-option-img" /> </button> <button className="dashboard__subject-option btn"> <img src={trash} alt="" className="dashboard__subject-option-img" /> </button> </div> </div> {/* 仅匹配当前编辑ID的科目才显示输入框 */} {editingSubjectId === subject._id && <div className="exam__subject-inputs"> <input value={subjectName} onChange={e => setSubjectName(e.target.value)} type="text" className="exam__subject-input grey-input" placeholder='Subject Name' /> <input value={achievableScore} onChange={e => setAchievableScore(e.target.value)} type="number" className="exam__subject-input grey-input" placeholder='Achivable Score' /> <input value={obtainedScore} onChange={e => setObtainedScore(e.target.value)} type="number" className="exam__subject-input grey-input" placeholder='Obtained Score' /> <button onClick={(e) => requestUpdateSubject(e, subject._id)} className="exam__subject-submit btn">Update</button> <button onClick={() => setEditingSubjectId(null)} className="exam__subject-submit btn">Cancel</button> </div> } <hr /> </React.Fragment> )} {!isLoading && <div className="exam__subject-inputs"> <input onChange={e => setSubjectName(e.target.value)} type="text" className="exam__subject-input grey-input" placeholder='Subject Name' /> <input onChange={e => setAchievableScore(e.target.value)} type="number" className="exam__subject-input grey-input" placeholder='Achivable Score' /> <input onChange={e => setObtainedScore(e.target.value)} type="number" className="exam__subject-input grey-input" placeholder='Obtained Score' /> <button onClick={requestAddSubject} className="exam__subject-submit btn">Add Subject</button> </div> } {isLoading && <div className="dashboard__error-message-container"> <h1 className="dashboard__error-message">Loading...</h1> </div> } <div className="exam__danger-zone"> <div className="exam__name-edit-container"> <div className="exam__name-edit"> <input type="text" className="exam__name-edit-input grey-input" placeholder='Change Name' /> </div> <button className='exam__edit-button btn'><img src={pencilToSquare} alt="" className="exam__edit-img" /></button> </div> <div className="exam__delete"> <button className="exam__delete-button">Delete Exam</button> </div> </div> </div> </> ) } export default Exam
关键修改说明
- 状态精准化:用
editingSubjectId替代全局open状态,精准跟踪当前编辑的科目 - 编辑数据预填充:点击编辑按钮时自动填充科目原有数据,提升编辑体验
- 条件渲染控制:通过ID匹配实现仅展开目标科目输入框
- 更新逻辑优化:新增PUT请求更新指定科目,成功后重置状态并重新拉取数据,避免页面刷新
- 新增取消功能:支持用户放弃编辑,快速重置状态
内容的提问来源于stack exchange,提问作者Goldfish1406
相关产品推荐
相关产品推荐

