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

更新Context后,以Context为依赖的useEffect未触发问题排查

问题:Context更新后useEffect未触发,自定义钩子状态不更新

在组件中使用自定义钩子管理状态,将Context中的transactions作为useEffect的依赖项以更新状态。但更新Context后,useEffect并未触发,通过控制台日志和React DevTools可确认Context值已更新。

相关代码

自定义钩子代码

export const useFilteredTrans = (
  type: string,
  transactions: Transaction[] | undefined
): Transaction[] | null => {
  const [filteredTrans, setFilteredTrans] = useState<Transaction[] | null>(null)
  useEffect(() => {
    if (transactions && transactions.length > 0) {
      const filtered = transactions.filter(
        (transaction) => transaction.type === type
      )
      setFilteredTrans(filtered)
    }
  }, [transactions])
  return filteredTrans
}

使用Context和自定义钩子的组件代码

export const Expenses = () => {
  const {
    currentAccount: { transactions },
  } = useContext(AccountContext)
  const expenses = useFilteredTrans('e', transactions)

  return (
    <div>
      <Module>
        <div className='mb-4 flex justify-between'>
          <h1>Expenses</h1>
          <ShowAddModalButton modalType='e' entityType='e' />
        </div>
        {expenses
          ? expenses.map((expenseItem) => {
              const { id, date, expense, credit, amount } = expenseItem
              return (
                <div className='grid grid-cols-5 gap-3' key={id}>
                  <p className='col-span-2'>{fmtDateString(date)}</p>
                  <p>{expense?.name}</p>
                  <p className='text-right'>{credit ? '+ $' : '- $'} </p>
                  <p className='text-right'>{amount}</p>
                </div>
              )
            })
          : null}
      </Module>
    </div>
  )
}

Context Reducer更新代码

case 'ADD_EXPENSE': {
  const account = state.currentAccount
  account.transactions?.unshift(action.payload)
  return { ...state, currentAccount: account, isLoading: false }
}

问题原因

核心问题出在Reducer的更新逻辑里:你直接修改了state.currentAccount的transactions数组(使用unshift),然后把原account对象放回了新状态中。

React的依赖比较是引用相等性检查:虽然数组内容变了,但transactions的引用地址没有改变,useEffect的依赖数组[transactions]认为值没有变化,所以不会触发回调。

解决方案

1. 修改Reducer,创建新的数组和对象

在Reducer中更新时,必须返回全新的引用,不能直接修改原状态对象:

case 'ADD_EXPENSE': {
  // 复制currentAccount对象,避免修改原状态
  const updatedAccount = { ...state.currentAccount };
  // 复制transactions数组并添加新元素,生成全新数组引用
  updatedAccount.transactions = updatedAccount.transactions 
    ? [action.payload, ...updatedAccount.transactions]
    : [action.payload];
  return { ...state, currentAccount: updatedAccount, isLoading: false };
}

这样transactions的引用会变成新的数组,useEffect的依赖检测到变化后就会触发,更新自定义钩子中的filteredTrans状态。

2. 简化自定义钩子(可选)

其实这个自定义钩子不需要维护额外的state,可以直接返回过滤后的结果,完全避免useEffect的依赖问题:

export const useFilteredTrans = (
  type: string,
  transactions: Transaction[] | undefined
): Transaction[] | null => {
  if (!transactions || transactions.length === 0) {
    return null;
  }
  return transactions.filter(transaction => transaction.type === type);
}

这样每次transactions更新时,组件会直接重新计算过滤结果,代码更简洁,也不会有依赖触发的问题。

内容的提问来源于stack exchange,提问作者externitee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 10:25:35