You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

关键修改说明

  1. 状态精准化:用editingSubjectId替代全局open状态,精准跟踪当前编辑的科目
  2. 编辑数据预填充:点击编辑按钮时自动填充科目原有数据,提升编辑体验
  3. 条件渲染控制:通过ID匹配实现仅展开目标科目输入框
  4. 更新逻辑优化:新增PUT请求更新指定科目,成功后重置状态并重新拉取数据,避免页面刷新
  5. 新增取消功能:支持用户放弃编辑,快速重置状态

内容的提问来源于stack exchange,提问作者Goldfish1406

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 17:42:02