React JS中如何实现薪资字段自动求和与输入框自动聚焦?
核心问题点
- 净薪资计算逻辑失效:年薪输入框的
onChange事件里错误将固定津贴、浮动津贴等4个字段统一赋值成年薪输入框的值,且这几个字段单独修改时没有触发净薪资重算;另外输入框默认返回字符串类型值,直接相加会触发字符串拼接而非数值求和 - 自动聚焦逻辑不完整:仅给年薪输入框绑定了选中效果,没有在组件挂载阶段触发聚焦动作,无法实现加载后自动定位到输入框的效果
- 表单绑定方式错误:所有输入项用
defaultValue做单向绑定,state更新后输入框不会同步展示最新计算值 - 提交逻辑冗余:提交按钮额外绑定的点击事件重复设置state无实际作用,也没有实现fetch提交的完整逻辑
修复后完整代码
import { useState, useRef, useEffect } from 'react'; export default function PayrollForm() { // 初始化薪资表单状态 const [payroll, setPayroll] = useState({ annual: '', monthly: 0, basic: 0, hra: 0, fixedAllowance: 0, variableAllowance: 0, employerShare: 0, statutoryBonus: 0, netSalary: 0 }); const annualInputRef = useRef(null); // 组件加载完成后自动聚焦年薪输入框 useEffect(() => { annualInputRef.current?.focus(); }, []); // 统一数值转换,避免字符串拼接问题 const toNumber = (val) => Number(val) || 0; // 统一净薪资计算逻辑 const calculateNetSalary = (formData) => { return Math.round( toNumber(formData.basic) + toNumber(formData.hra) + toNumber(formData.fixedAllowance) + toNumber(formData.variableAllowance) + toNumber(formData.employerShare) + toNumber(formData.statutoryBonus) ); }; // 年薪输入联动计算逻辑 const onAnnualChange = (e) => { const annual = toNumber(e.target.value); const monthly = Math.round(annual / 12); const basic = Math.round((monthly / 100) * 50); const hra = Math.round((basic / 100) * 50); const updatedPayroll = { ...payroll, annual, monthly, basic, hra }; updatedPayroll.netSalary = calculateNetSalary(updatedPayroll); setPayroll(updatedPayroll); }; // 其他可编辑薪资项的统一变更处理 const onAllowanceChange = (e, field) => { const value = toNumber(e.target.value); const updatedPayroll = { ...payroll, [field]: value }; updatedPayroll.netSalary = calculateNetSalary(updatedPayroll); setPayroll(updatedPayroll); }; // 表单提交逻辑 const addPayroll = async (e) => { e.preventDefault(); try { const res = await fetch('/api/payroll', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payroll) }); if (res.ok) { // 提交成功后可自定义重置表单、弹出提示等逻辑 alert('薪资数据提交成功'); } } catch (err) { console.error('提交失败:', err); } }; return ( <div className='payroll_main_container'> <form onSubmit={addPayroll}> <div className='form_group'> <label>Annual Salary</label> <input type='number' className='form-control' id='annual' value={payroll.annual} onChange={onAnnualChange} ref={annualInputRef} onFocus={() => { annualInputRef.current.select(); }} placeholder='Annual' /> <label>Monthly Salary</label> <input type='number' className='form-control' id='monthly' value={payroll.monthly} readOnly placeholder='Monthly' /> <label>Basic Salary</label> <input type='number' className='form-control' id='basic' value={payroll.basic} readOnly placeholder='Basic' /> <label>HRA</label> <input type='number' className='form-control' id='hra' value={payroll.hra} readOnly placeholder='HRA' /> <label>Fixed Allowance</label> <input type='number' className='form-control' id='fixedAllowance' onChange={(e) => onAllowanceChange(e, 'fixedAllowance')} value={payroll.fixedAllowance} placeholder='Fixed Allowance' /> <label>Variable Allowance</label> <input type='number' className='form-control' id='variableAllowance' onChange={(e) => onAllowanceChange(e, 'variableAllowance')} value={payroll.variableAllowance} placeholder='Variable Allowance' /> <label>Employer Share</label> <input type='number' className='form-control' id='employerShare' onChange={(e) => onAllowanceChange(e, 'employerShare')} value={payroll.employerShare} placeholder='Employer Share' /> <label>Statutory Bonus</label> <input type='number' className='form-control' id='statutoryBonus' onChange={(e) => onAllowanceChange(e, 'statutoryBonus')} value={payroll.statutoryBonus} placeholder='Statutory Bonus' /> <label>Net Salary</label> <input type='number' className='form-control' id='netSalary' value={payroll.netSalary} readOnly placeholder='Net Salary' /> <button type='submit' className='btn btn-primary'>Add</button> </div> </form> </div> ) }
关键修改说明
- 所有输入项从非受控的
defaultValue绑定改为value受控绑定,自动计算的月工资、基本工资、HRA、净薪资字段添加readOnly属性禁止手动修改,输入框类型统一设为number从输入层限制非数字内容 - 抽离通用数值转换方法,所有输入值统一转为数字类型后再参与计算,从根源避免字符串拼接问题
- 抽离独立的净薪资计算方法,年薪联动计算、津贴项修改时都会触发净薪资重算,所有关联项填写完成后净薪资会自动展示正确求和结果
- 新增
useEffect钩子在组件挂载完成后触发年薪输入框的聚焦动作,实现加载自动定位效果 - 移除提交按钮上冗余的点击事件,在表单提交事件中实现完整的fetch POST提交逻辑,默认阻止表单原生跳转行为,按JSON格式序列化表单数据提交
内容的提问来源于stack exchange,提问作者Imtiyaz Sheikh
相关产品推荐
相关产品推荐

