React中useEffect依赖更新仅触发一次,如何同步更新total值?
问题
我正在开发一款支出追踪器(Expense Tracker),希望在props传入的totalIncome和totalExpense作为useEffect依赖项更新时,同步修改total金额,但目前useEffect仅触发一次。以下是我的代码:
const Main = ({ getData, totalIncome, totalExpense }) => { const [total, setTotal] = useState(parseInt(0)) const [type, setType] = useState('income') const [date, setDate] = useState('') const [amount, setAmount] = useState('') const [category, setCategory] = useState('Rent') const [income, setIncome] = useState('') const [id, setId] = useState(Date.now()) const changeType = (e) =>{ setType(e.target.value) } const handleDate = (e) =>{ setDate(e.target.value) } const handleAmount = (e) =>{ setAmount(parseInt(e.target.value)) } const handleCategory = (e) =>{ setCategory(e.target.value) } const handleIncome = (e) =>{ setIncome(e.target.value) } const handleSubmit = (e) =>{ e.preventDefault() setId(Date.now()) getData({total, type, date, amount, category , income, id}) type === 'income' ? setTotal(parseInt(total + amount)) : setTotal(parseInt(total - amount)) setAmount('') setIncome('') setDate('') } useEffect(()=>{ if(!category){ setCategory('Rent') } }, [category]) useEffect(() => { setTotal(prev => prev + totalExpense) }, [totalExpense]); useEffect(() => { setTotal(prev => prev - totalIncome) }, [totalIncome]); return ( <div className={main.container}> <h1 className={main.headline}>Expense Tracker</h1> <h2>{total >= 0 ? `Total Balance: $${total}` : `Your debt: $${total}`}</h2> <hr style={{margin: '20px 0'}}/> <form className={main.form} onSubmit={handleSubmit}> <div className={main.typeBox}> <div> <label htmlFor='type'>Type:</label> <select id='type' name='type' value={type} onChange={changeType} > <option value="expense">Expense</option> <option value="income">Income</option> </select> </div> <div> {type === 'income' && ( <div> <label>Income:</label> <input type='text' value={income} onChange={handleIncome}/> </div> )} {type === 'expense' && ( <div> <label htmlFor='category'>Category:</label> <select onChange={handleCategory}> <option value='Rent'>Rent</option> <option value='Food'>Food</option> <option value='Transport'>Transport</option> <option value='Health'>Health</option> <option value='Utility'>Utility</option> <option value='Other'>Other</option> </select> </div> )} </div> </div> { /* Amount and date section */} <div className={main.amountDate}> <div> <label>Amount:</label> <input type="number" value={amount} onChange={handleAmount} required/> </div> <div> <label>Date:</label> <input type="date" value={date} onChange={handleDate} required/> </div> </div> <button type='submit' className={main.create}>CREATE</button> </form> </div> ) } export default Main
解决方案
核心问题
你当前的两个useEffect逻辑错误:每次totalExpense更新时会累加旧值,totalIncome更新时会累减旧值,这不是基于最新总收入/总支出的正确计算,导致后续依赖更新时无法同步正确的total值。
方案1:直接计算(推荐,无状态不同步问题)
total本质是总收入 - 总支出的计算结果,完全不需要用useState维护,直接在渲染时计算即可,确保和props完全同步:
- 移除
total的useState定义:
// 删掉这一行 // const [total, setTotal] = useState(parseInt(0))
- 修改渲染时的
h2内容:
<h2>{totalIncome - totalExpense >= 0 ? `Total Balance: $${totalIncome - totalExpense}` : `Your debt: $${totalIncome - totalExpense}`}</h2>
- 移除
handleSubmit里的setTotal逻辑,因为total现在由父组件的totalIncome和totalExpense驱动,提交后只需让父组件更新这两个值即可:
const handleSubmit = (e) =>{ e.preventDefault() setId(Date.now()) getData({type, date, amount, category , income, id}) setAmount('') setIncome('') setDate('') }
方案2:用useEffect正确同步state
如果一定要维护total的state,需要修改useEffect逻辑,每次totalIncome或totalExpense变化时,直接用最新的两个值计算total:
- 初始化
total为正确的初始值:
const [total, setTotal] = useState(totalIncome - totalExpense)
- 合并两个useEffect为一个,同时监听两个依赖:
// 删掉原来的两个单独useEffect,替换为这个 useEffect(() => { setTotal(totalIncome - totalExpense) }, [totalIncome, totalExpense])
- 同样需要移除
handleSubmit里的setTotal逻辑,避免组件内部修改total导致和props同步的逻辑冲突。
额外优化
- 修复
handleAmount的空值问题:
const handleAmount = (e) =>{ setAmount(e.target.value ? parseInt(e.target.value) : '') }
- 修复category下拉框的选中状态不同步问题:
<select value={category} onChange={handleCategory}> {/* options不变 */} </select>
内容的提问来源于stack exchange,提问作者M Hasan
相关产品推荐
相关产品推荐

