React useEffect触发updateChart执行两次致图表数据重复如何解决
问题根因
初始化阶段函数执行两次是两个问题叠加导致的:
- React 18 开发环境默认开启严格模式,首次加载组件时会主动执行一轮「挂载→卸载→重挂载」流程,检测副作用写法是否规范,这会让依赖项为初始值的
useEffect执行两次。 - 你的代码本身存在副作用设计漏洞:
- 图表配置
data被定义在组件外部,属于模块级共享变量,所有组件实例操作的都是同一份数据引用 updateChart逻辑是直接往原有数据集里push新值,执行前从不清空旧数据,只要函数重复调用必然会重复追加相同数据。就算没有严格模式的双调用,后续userData.transactions更新时,你也会把已经存在的旧交易数据重复推入数组,一样会出现数据重复问题。
- 图表配置
修复方法
不需要关闭严格模式,只要把数据更新逻辑改成「全量生成新数据」而非「累加旧数据」,就能保证副作用不管执行多少次,最终结果都一致:
- 把图表数据移入组件内部维护,不要放在组件外作为全局变量
- 重写数据更新逻辑,每次执行时直接根据最新的
userData.transactions生成完整数据集,替换原有内容而非追加 - 保证同一份输入下,副作用多次执行的输出结果完全一致
修复后的完整组件代码:
import React, { useEffect, useState, useMemo } from 'react'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler, } from 'chart.js'; import { Line } from 'react-chartjs-2'; ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler ); const options = { responsive: true, plugins: { legend: { position: 'top', }, title: { display: true, text: 'Chart.js Line Chart', }, }, }; const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July']; const ChartHistory = ({userData}) => { // 把图表数据移到组件内部维护 const [chartData, setChartData] = useState({ labels, datasets: [ { fill: true, label: 'Dataset 1', data: [], borderColor: 'rgb(53, 162, 235)', backgroundColor: 'rgba(53, 162, 235, 0.5)', }, ], }); useEffect(() => { // 每次更新直接生成新的data数组,不做push累加,从根源避免重复 setChartData(prev => ({ ...prev, datasets: prev.datasets.map((dataset, idx) => { if (idx !== 0) return dataset; return { ...dataset, // 直接用transactions生成新数组,完全替换旧值 data: userData.transactions.map(item => item.amount) } }) })) }, [userData.transactions]) return ( <div> <Line options={options} data={chartData} /> </div> ) } export default ChartHistory
更优实现:图表数据完全是从
userData.transactions派生的值,根本不需要用useEffect手动维护,直接用useMemo派生即可,代码更简洁,也不会有副作用重复执行的问题:const chartData = useMemo(() => ({ labels, datasets: [ { fill: true, label: 'Dataset 1', data: userData.transactions.map(item => item.amount), borderColor: 'rgb(53, 162, 235)', backgroundColor: 'rgba(53, 162, 235, 0.5)', }, ], }), [userData.transactions])这种写法完全没有手动操作副作用的逻辑,不管组件重渲染多少次,只要
userData.transactions不变,生成的图表数据就完全一致,不会出现重复值。
内容的提问来源于stack exchange,提问作者awfha
相关产品推荐
相关产品推荐

