React遍历列表时实现单个输入框独立状态绑定的技术问题咨询
解决React列表中输入框同步更新的问题
嘿,我一眼就看出问题所在了——你所有的编辑输入框都绑定了同一个全局状态变量(day、from、until),不管你编辑哪一项,修改的都是这几个全局值,自然所有输入框都会同步显示相同内容。咱们来调整一下,让每个输入框只和对应的训练项绑定:
核心思路
不再用全局的day/from/until来存储编辑中的值,而是为当前正在编辑的训练项单独维护临时状态,这样每个输入框只会响应对应项的编辑操作。
修改后的完整代码
import React, { useState, useEffect } from 'react' import axios from 'axios' function Training({ teamid }) { const [isTrainingExisting, setIsTrainingExisting] = useState(false); const [trainingData, setTrainingData] = useState([]); const [addTraining, setAddTraining] = useState(false); // 拆分新增和编辑的状态:新增训练用独立的全局状态 const [newDay, setNewDay] = useState(''); const [newFrom, setNewFrom] = useState(''); const [newUntil, setNewUntil] = useState(''); const getTrainingData = () => { axios .get(`${process.env.REACT_APP_API_URL}/team/team_training-${teamid}`) .then((res) => { if (res.status === 200) { if (res.data?.length > 0) { setIsTrainingExisting(true) setTrainingData(res.data) } else { setIsTrainingExisting(false) } } }) .catch((error) => { console.log(error); }); } useEffect(() => { getTrainingData(); }, []); const deleteTraining = (id) => { axios .delete(`${process.env.REACT_APP_API_URL}/team/delete/team_training-${teamid}`, { data: { trainingsid: `${id}` } }) .then((res) => { if (res.status === 200) { const updatedTrainings = trainingData.filter(obj => obj.trainingsid !== id); setTrainingData([...updatedTrainings]); } }) .catch((error) => { console.log(error); }); } const addNewTraining = () => { setAddTraining(true); } const addTrainingNew = () => { axios .post(`${process.env.REACT_APP_API_URL}/team/add/team_training-${teamid}`, { von: newFrom, bis: newUntil, tag: newDay }) .then((res) => { if (res.status === 200) { setAddTraining(false) const newTraining = { trainingsid: res.data, mannschaftsid: teamid, von: newFrom, bis: newUntil, tag: newDay } setTrainingData([...trainingData, newTraining]); // 清空新增表单 setNewDay(''); setNewFrom(''); setNewUntil(''); } }) .catch((error) => { console.log(error); }); } // 用对象存储当前编辑的项ID和临时值,初始为null const [editingItem, setEditingItem] = useState(null); const editingTraining = (training) => { // 点击编辑时,把当前项的原始数据存入临时状态 setEditingItem({ id: training.trainingsid, tag: training.tag, von: training.von, bis: training.bis }); }; const updateTraining = () => { if (!editingItem) return; // 调用API更新训练数据 axios .put(`${process.env.REACT_APP_API_URL}/team/update/team_training-${teamid}`, { trainingsid: editingItem.id, von: editingItem.von, bis: editingItem.bis, tag: editingItem.tag }) .then((res) => { if (res.status === 200) { // 更新本地trainingData,替换已编辑的项 const updatedTrainings = trainingData.map(item => item.trainingsid === editingItem.id ? {...item, ...editingItem} : item ); setTrainingData(updatedTrainings); // 退出编辑状态 setEditingItem(null); } }) .catch((error) => { console.log(error); }); } return ( <div> {trainingData.map((d) => ( <div key={d.trainingsid}> Trainingszeiten {/* 输入框绑定逻辑:仅当前编辑项显示临时值,否则显示原始数据 */} <input className="input is-normal" type="text" value={editingItem?.id === d.trainingsid ? editingItem.tag : d.tag} placeholder="Wochentag" onChange={(event) => { if (editingItem?.id === d.trainingsid) { // 更新临时编辑状态时保留其他字段 setEditingItem({...editingItem, tag: event.target.value}); } }} disabled={editingItem?.id !== d.trainingsid} /> <input className="input is-normal" type="text" value={editingItem?.id === d.trainingsid ? editingItem.von : d.von} placeholder="Von" onChange={(event) => { if (editingItem?.id === d.trainingsid) { setEditingItem({...editingItem, von: event.target.value}); } }} disabled={editingItem?.id !== d.trainingsid} /> <input className="input is-normal" type="text" value={editingItem?.id === d.trainingsid ? editingItem.bis : d.bis} placeholder="Bis" onChange={(event) => { if (editingItem?.id === d.trainingsid) { setEditingItem({...editingItem, bis: event.target.value}); } }} disabled={editingItem?.id !== d.trainingsid} /> {/* 非编辑状态下显示原始数据 */} {editingItem?.id !== d.trainingsid && ( <span>{d.tag} - {d.von} bis {d.bis} Uhr</span> )} <button className="button is-danger" onClick={() => deleteTraining(d.trainingsid)}>Löschen</button> {editingItem?.id === d.trainingsid ? ( <button className="button is-success" onClick={updateTraining}>Save</button> ) : ( <button className="button is-info" onClick={() => editingTraining(d)}>Edit</button> )} <br /> </div> ))} {/* 渲染新增训练的表单(你之前的代码遗漏了这部分) */} {addTraining && ( <div> <input className="input is-normal" type="text" value={newDay} placeholder="Wochentag" onChange={(e) => setNewDay(e.target.value)} /> <input className="input is-normal" type="text" value={newFrom} placeholder="Von" onChange={(e) => setNewFrom(e.target.value)} /> <input className="input is-normal" type="text" value={newUntil} placeholder="Bis" onChange={(e) => setNewUntil(e.target.value)} /> <button className="button is-success" onClick={addTrainingNew}>Hinzufügen</button> <button className="button is-light" onClick={() => setAddTraining(false)}>Abbrechen</button> </div> )} <button className="button is-primary" onClick={addNewTraining}>Neues Training hinzufügen</button> </div> ) } export default Training
关键改动说明
- 拆分状态:把新增训练和编辑训练的状态完全分开,新增用独立的全局状态,编辑用
editingItem对象存储当前项的临时值,避免状态冲突。 - 输入框独立绑定:每个输入框仅在对应项处于编辑状态时,绑定
editingItem里的临时值,非编辑状态显示原始数据,且只有编辑中的输入框可操作。 - 优化列表key:把列表的
key从索引i改成唯一标识d.trainingsid,避免列表更新时出现不必要的渲染错误。 - 完善编辑流程:点击编辑时初始化临时值,保存时同步更新API和本地数据,确保状态一致性。
这样修改后,每个输入框就只会和对应的训练项绑定,编辑操作再也不会影响其他输入框啦!
内容的提问来源于stack exchange,提问作者Mr. Hankey
相关产品推荐
相关产品推荐

