React组件props变化时更新列表触发无限重渲染问题求解
问题原因分析
- 无限重渲染的根因:你把状态更新逻辑
setExpenses直接写在了组件的渲染主流程中,每次组件渲染只要满足判断条件就会触发状态更新,状态更新又会触发组件重新渲染,形成死循环。 - 直接修改props不生效:React 中 props 是只读属性,你在子组件内执行
props.newExpense = null的操作不会实际修改父组件传递下来的属性值,所以判断条件会一直成立。
解决方法
步骤1:用useEffect监听props.newExpense的变化
把新增 expense 的逻辑放到 useEffect 中,只有当props.newExpense发生变化且符合新增条件时才触发状态更新,避免每次渲染都执行判断逻辑。
步骤2:通过父组件回调清空newExpense
父组件传递newExpense的同时,需要额外传递一个清空newExpense的回调函数,子组件完成新增逻辑后调用该回调,通知父组件重置newExpense的值。
修改后的代码如下:
import { useState, useEffect } from 'react'; export default function Expenses(props) { const [expenses, setExpenses] = useState(INITIAL_EXPENSES); const [filteredYear, setFilteredYear] = useState('2020'); const filterExpenses = (date) => { setFilteredYear(date); } // 仅当props.newExpense变化时执行新增逻辑 useEffect(() => { if (props.newExpense?.hasOwnProperty('expenseTitle')) { setExpenses((prev) => { return [...prev, props.newExpense]; }) // 调用父组件传入的清空回调,重置父组件中的newExpense props.clearNewExpense?.(); } }, [props.newExpense, props.clearNewExpense]) const filteredExpenses = expenses.filter(exp => exp.expenseDate.getFullYear().toString() === filteredYear); return ( <Card className="expenses"> <ExpensesFilter year={filteredYear} onExpensesFilter={filterExpenses}/> <ExpenseChart expenses={filteredExpenses}/> <ExpensesList items={filteredExpenses}/> </Card> ) }
父组件侧需要同步修改两个点:
- 传递
clearNewExpense回调函数给Expenses组件,函数逻辑为把父组件中存储的newExpense重置为null/空值- 确保每次新增expense时传递给Expenses组件的
newExpense是新的引用值,避免useEffect无法监听到变化
内容的提问来源于stack exchange,提问作者Jan Testowy
相关产品推荐
相关产品推荐

