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

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完全同步:

  1. 移除total的useState定义:
// 删掉这一行
// const [total, setTotal] = useState(parseInt(0))
  1. 修改渲染时的h2内容:
<h2>{totalIncome - totalExpense >= 0 ? `Total Balance: $${totalIncome - totalExpense}` : `Your debt: $${totalIncome - totalExpense}`}</h2>
  1. 移除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:

  1. 初始化total为正确的初始值:
const [total, setTotal] = useState(totalIncome - totalExpense)
  1. 合并两个useEffect为一个,同时监听两个依赖:
// 删掉原来的两个单独useEffect,替换为这个
useEffect(() => {
  setTotal(totalIncome - totalExpense)
}, [totalIncome, totalExpense])
  1. 同样需要移除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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 07:35:18