React中如何为特定输入框显示错误提示?
问题背景
我有一个存储产品信息的 state:
const [rowsData, setRowsData] = useState([ { width: "", height: "", quantity: "" }, ]);
还有一个支持按需添加/删除行的表格:
return ( <> {rowsData.map((data, index) => { const { width, height, quantity} = data; return ( <tr key={index}> <td>{index + 1}</td> <td> <input type="number" value={width} onChange={(evnt) => handleChange(index, evnt)} name="width" className={wError ? "input input-error border-2 input-bordered w-full max-w-xs" : "input input-bordered w-full max-w-xs"} /> </td> <td> <input type="number" value={height} onChange={(evnt) => handleChange(index, evnt)} name="height" className={hError ? "input input-error border-2 input-bordered w-full max-w-xs" : "input input-bordered w-full max-w-xs"} />{" "} </td> <td> <input type="number" value={quantity} onChange={(evnt) => handleChange(index, evnt)} name="quantity" className={qError ? "input input-error border-2 input-bordered w-full max-w-xs" : "input input-bordered w-full max-w-xs"} />{" "} </td> <td> {index !==0 ? <button className="btn" onClick={() => deleteTableRows(index)}> x </button>: null} </td> </tr> ); })}
处理输入变化的 handleChange 函数:
const handleChange = (index, evnt) => { const { name, value } = evnt.target; const rowsInput = [...rowsData]; rowsInput[index][name] = value; setRowsData(rowsInput); };
需求:提交时所有字段必须仅接受数字,若输入非数字,对应输入框需显示红色错误边框。
我尝试的解决方案:
- 为每个字段创建单独的错误 state:
const [wError, setWError] = useState(false); const [hError, setHError] = useState(false); const [qError, setQError] = useState(false);
- 检查每个输入框的值是否为数字:
const createOrder = () => { let wCheck, hCheck, qCheck rowsData.map((e) => { wCheck = !isNaN(e.width) && !isNaN(parseFloat(e.width)); hCheck = !isNaN(e.height) && !isNaN(parseFloat(e.height)); qCheck = !isNaN(e.quantity) && !isNaN(parseFloat(e.quantity)); }); };
- 设置错误状态:
setWError(!wCheck); setHError(!hCheck); setQError(!qCheck);
- 根据错误状态为输入框添加样式(如上述表格代码所示)
问题
这个方案存在严重 bug:错误状态仅由最后一行的输入决定,导致整列要么全标红,要么全显示正常,无法定位到具体某一行的单个输入框。请问如何修改才能让错误提示精准对应到单个输入框?
修复方案
核心问题是你用了三个全局错误变量,无法区分不同行的错误。正确的做法是维护一个与 rowsData 结构完全对应的错误状态数组,让错误信息和每行数据绑定。
步骤1:初始化对应结构的错误 state
将错误状态改成数组,每个元素对应一行的三个字段错误情况:
const [rowErrors, setRowErrors] = useState([ { width: false, height: false, quantity: false } ]);
步骤2:完善提交验证逻辑
在 createOrder 中逐行逐字段验证,生成对应的错误数组:
const createOrder = () => { // 生成和rowsData匹配的错误数组 const newErrors = rowsData.map(row => ({ width: isNaN(row.width) || isNaN(parseFloat(row.width)), height: isNaN(row.height) || isNaN(parseFloat(row.height)), quantity: isNaN(row.quantity) || isNaN(parseFloat(row.quantity)) })); setRowErrors(newErrors); // 检查是否存在错误,无错误时执行提交逻辑 const hasError = newErrors.some(err => err.width || err.height || err.quantity); if (!hasError) { // 这里编写提交订单的业务逻辑 console.log("提交数据:", rowsData); } };
步骤3:修改表格输入框的样式绑定
让每个输入框的错误样式对应当前行的字段错误状态:
{rowsData.map((data, index) => { const { width, height, quantity} = data; const currentRowErrors = rowErrors[index]; // 获取当前行的错误信息 return ( <tr key={index}> <td>{index + 1}</td> <td> <input type="number" value={width} onChange={(evnt) => handleChange(index, evnt)} name="width" className={currentRowErrors.width ? "input input-error border-2 input-bordered w-full max-w-xs" : "input input-bordered w-full max-w-xs"} /> </td> <td> <input type="number" value={height} onChange={(evnt) => handleChange(index, evnt)} name="height" className={currentRowErrors.height ? "input input-error border-2 input-bordered w-full max-w-xs" : "input input-bordered w-full max-w-xs"} />{" "} </td> <td> <input type="number" value={quantity} onChange={(evnt) => handleChange(index, evnt)} name="quantity" className={currentRowErrors.quantity ? "input input-error border-2 input-bordered w-full max-w-xs" : "input input-bordered w-full max-w-xs"} />{" "} </td> <td> {index !==0 ? <button className="btn" onClick={() => deleteTableRows(index)}> x </button>: null} </td> </tr> ); })}
步骤4:同步维护添加/删除行的错误状态
确保添加或删除行时,错误数组和数据数组同步更新:
// 添加行函数示例 const addTableRow = () => { setRowsData(prev => [...prev, { width: "", height: "", quantity: "" }]); setRowErrors(prev => [...prev, { width: false, height: false, quantity: false }]); }; // 删除行函数示例 const deleteTableRows = (index) => { setRowsData(prev => prev.filter((_, i) => i !== index)); setRowErrors(prev => prev.filter((_, i) => i !== index)); };
可选优化:实时验证
如果想在用户输入时就实时提示错误,可以修改 handleChange 函数:
const handleChange = (index, evnt) => { const { name, value } = evnt.target; const rowsInput = [...rowsData]; rowsInput[index][name] = value; setRowsData(rowsInput); // 实时验证当前修改的字段 const newErrors = [...rowErrors]; newErrors[index][name] = isNaN(value) || isNaN(parseFloat(value)); setRowErrors(newErrors); };
内容的提问来源于stack exchange,提问作者TF Ryzen
相关产品推荐
相关产品推荐

