更新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
相关产品推荐
相关产品推荐

