React传递对象数组类型props给子组件时值翻倍异常
问题背景
实现月度支出进度条功能,共涉及Chart、Bar两个组件,其中Chart组件初始实现代码如下:
import Bar from "./Bar"; const INIT_MONTH_EXPENSES = [ { label: "Jan", value: 0 }, { label: "Feb", value: 0 }, { label: "Mar", value: 0 }, { label: "Apr", value: 0 }, { label: "May", value: 0 }, { label: "Jun", value: 0 }, { label: "Jul", value: 0 }, { label: "Aug", value: 0 }, { label: "Sep", value: 0 }, { label: "Oct", value: 0 }, { label: "Nov", value: 0 }, { label: "Dec", value: 0 }, ]; const Chart = (props) => { const totalExpenseAmountOfYear = props.items.reduce((total, item) => { total = +total + +item.amount; return total; }, 0); for (const expense of props.items) { const expenseMonth = expense.date.getMonth(); INIT_MONTH_EXPENSES[expenseMonth].value += expense.amount; } //INIT_MONTH_EXPENSES[2].value give me the expected value but props.expenses[2].value in the Bar component give me a wrong value(actually doubled value) return ( <div className="bg-pink-200 w-full h-[150px] p-4 mb-8 flex"> <Bar expenses={INIT_MONTH_EXPENSES} totalAmount={totalExpenseAmountOfYear} /> </div> ); }; export default Chart;
遇到的异常现象:
- 在
Chart组件内部直接读取INIT_MONTH_EXPENSES[2].value可以得到预期正确值 - 将
INIT_MONTH_EXPENSES作为expenses属性传递给Bar子组件后,Bar组件内读取props.expenses拿到的数值为预期值的两倍,结果错误
问题根因
INIT_MONTH_EXPENSES是定义在组件外部的模块级变量,属于全局共享引用:
- React组件每次触发重渲染时,都会重新执行组件内部的逻辑,写在组件内的for循环累加逻辑会反复执行
- 由于没有在每次累加前重置月度数据的value为0,每次重渲染都会在上一次计算的value基础上继续累加数值
- JS中对象和数组是引用传递,传给Bar组件的是这个全局数组的引用,当全局数组被反复修改累加时,子组件拿到的props值就会出现翻倍、多倍增长的错误
- 在Chart组件里打印时值看起来正确,只是因为打印时机刚好在本轮for循环执行完成后,还没触发下一轮重渲染累加而已
修复方案
- 将月度初始数据的定义移到
Chart组件内部,保证每次组件渲染时都会生成一份全新的、value全为0的初始数据,避免全局变量残留旧值 - 不要直接修改外部共享的对象/数组,所有计算逻辑基于当前渲染周期内的临时数据完成,避免引用共享导致的异常
修复后的代码如下:
import Bar from "./Bar"; const Chart = (props) => { // 每次组件渲染重新生成初始月度数据,初始值全为0 const monthExpenses = [ { label: "Jan", value: 0 }, { label: "Feb", value: 0 }, { label: "Mar", value: 0 }, { label: "Apr", value: 0 }, { label: "May", value: 0 }, { label: "Jun", value: 0 }, { label: "Jul", value: 0 }, { label: "Aug", value: 0 }, { label: "Sep", value: 0 }, { label: "Oct", value: 0 }, { label: "Nov", value: 0 }, { label: "Dec", value: 0 }, ]; const totalExpenseAmountOfYear = props.items.reduce((total, item) => { return +total + +item.amount; }, 0); for (const expense of props.items) { const expenseMonth = expense.date.getMonth(); monthExpenses[expenseMonth].value += expense.amount; } return ( <div className="bg-pink-200 w-full h-[150px] p-4 mb-8 flex"> <Bar expenses={monthExpenses} totalAmount={totalExpenseAmountOfYear} /> </div> ); }; export default Chart;
内容的提问来源于stack exchange,提问作者amir_70
相关产品推荐
相关产品推荐

