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

React useState变量未调用setState却在计算时被意外修改

React状态变量意外被修改的原因及修复方案

问题描述

在React函数组件中使用useState存储数据,需要将数据转换单位后传入子组件,但每次组件重渲染时,状态变量priceData中的y值都会被自动除以1000——即便没有调用setPriceData,也没有直接修改priceData本身。

最小可复现代码

import React, { useEffect, useState } from "react"
import MyComponent from "MyComponent"

const PriceGraph = () => {
  const [priceData, setPriceData] = useState<Array<{ x: Date, y: number }>>([])

  useEffect(() => {
    const data = [
      {
        x: new Date("2023-04-04T13:00:00"),
        y: 108.71
      },
      {
        x: new Date("2023-04-04T14:00:00"),
        y: 112.73
      }
    ]
    setPriceData(data)
  }, [])

  return (
    <div>
      <MyComponent data={getChartData(priceData)} />
      <span>
        {priceData[0]?.y}
      </span>
    </div>
  )
}

function getChartData(data: Array<{ x: Date, y: number }>) {
  return {
    datasets: [
      {
        data: mwhToKwh(data),
      },
    ],
  }
}

function mwhToKwh(data: Array<{ x: Date, y: number }>) {
  return data.map(point => {
    point.y /= 1000
    return point
  })
}

export default PriceGraph

问题原因

核心问题是直接修改了状态中引用类型的内部属性:

  • priceData是存储在useState中的数组,属于引用类型,数组里的每个point对象都是原数据的引用。
  • 在mwhToKwh函数中,point.y /= 1000是直接修改了原对象的y属性,而非创建新对象。这会直接改变priceData状态中存储的对象值,因为它们指向同一个内存地址。
  • 每次组件重渲染时,getChartData都会被调用,进而执行mwhToKwh,导致原对象的y值被反复除以1000。

React要求状态遵循“不可变”原则——不是完全不能修改,而是不能直接修改状态的引用或其内部属性,必须通过setState更新引用。但这里的修改绕过了setState,直接改变了状态的实际内容,所以每次重渲染都会读取到修改后的值。

修复方案

避免直接修改原状态对象,而是在转换时创建新的对象:

修改mwhToKwh函数,使用对象扩展运算符返回新对象:

function mwhToKwh(data: Array<{ x: Date, y: number }>) {
  return data.map(point => {
    // 复制原对象的所有属性,同时替换y值,不修改原对象
    return {
      ...point,
      y: point.y / 1000
    }
  })
}

也可以使用Object.assign实现同样的效果:

function mwhToKwh(data: Array<{ x: Date, y: number }>) {
  return data.map(point => Object.assign({}, point, { y: point.y / 1000 }))
}

这样每次单位转换都是基于原数据生成新的对象数组,不会影响priceData状态中的原始数据,span里的数值就能保持初始值。

额外优化

如果getChartData的计算逻辑较复杂,或者组件重渲染频繁,可以用useMemo缓存计算结果,避免重复计算:

import React, { useEffect, useState, useMemo } from "react"

const PriceGraph = () => {
  // ... 其他代码不变

  // 仅当priceData变化时才重新计算chartData
  const chartData = useMemo(() => getChartData(priceData), [priceData])

  return (
    <div>
      <MyComponent data={chartData} />
      <span>
        {priceData[0]?.y}
      </span>
    </div>
  )
}

内容的提问来源于stack exchange,提问作者Axel Köhler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 10:55:13