React中Axios GET请求后useState无法更新数据问题求助
问题分析与修复方案
核心问题
你的代码里有几个关键问题导致状态无法正确更新:
- 顶层变量复用污染:
formatted和regressionData定义在组件顶层,每次组件渲染或请求时不会重置,会累积之前的数据,导致最终传给setData的是脏数据。 - 错误判断逻辑错误:
res.data.chart.error != 'null'的判断逻辑不对,API返回的error如果是null(类型是null),和字符串'null'比较永远为true,应该直接判断error是否不存在。 - 异步状态更新的误解:
setData是异步操作,调用后立刻console.log(data)拿的还是旧值,会误以为状态没更新。
修复后的代码
import React, { useState, useEffect }from 'react' import ChartBox from './ChartBox' import { Box, useMediaQuery, Button, Typography, InputLabel, MenuItem, FormControl, Select } from '@mui/material' import FlexBetween from './FlexBetween' import { CartesianGrid, Legend, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis, Bar, BarChart, ScatterChart, Scatter, Label, ComposedChart } from 'recharts' import regression, { DataPoint } from 'regression' import axios from 'axios' const Regression = () => { const [isPredictions, setIsPredictions] = useState(false) const [data, setData] = useState([]) const [stock, setStock] = useState('AAPL') const handleChange = (e) => { setStock(e.target.value) setData([]) } const stocks = ['AAPL', 'MSFT', 'GOOG', 'AMZN', 'NVDA', 'TSLA', 'META', 'XOM', 'WMT', 'JPM', 'KO', 'BAC', 'CSCO', 'MCD', 'PFE', 'NFLX', 'AMD', 'DIS', 'INTC', 'GE'] const menuItems = stocks.map(stock => <MenuItem key={stock} value={stock}>{stock}</MenuItem>) const formatData = (x, high, low) => { return { x, high, low } } const regressionDataFormatted = (i, avg) => { return [i, avg] } const regressionCoordinatesFormatted = (x, y) => { return { x, y } } useEffect(() => { // 把变量移到useEffect内部,每次请求重新初始化 let formatted = [] let regressionData = [] let timestamps = [] let stockData = [] const options = { method: 'GET', url: `https://yfapi.net/v8/finance/chart/${stock}`, headers: { 'x-api-key': '<api key>' }, params: { range: '6mo', region: 'US', interval: '1d', ticker: stock } } axios.request(options).then((res) => { // 修正错误判断:检查是否有result且无error const chartResult = res.data.chart.result?.[0] if (!res.data.chart.error && chartResult) { stockData = chartResult.indicators.quote[0] timestamps = chartResult.timestamp for (let i=0; i<100; i++) { if (!timestamps[i] || !stockData.high[i] || !stockData.low[i]) continue; // 避免undefined数据 const date = new Date(timestamps[i]*1000) const formattedDate = `${date.getMonth()+1}-${date.getDate()}` // 月份+1,避免0-11的问题 const high = stockData.high[i] const low = stockData.low[i] const avg = Math.floor((high + low)/2) const curr = formatData(formattedDate, high, low) const currRegression = regressionDataFormatted(i, avg) formatted.push(curr) regressionData.push(currRegression) } const regressionCoefficients = regression.linear(regressionData) for (let i=0; i<regressionCoefficients.points.length; i++) { const curr = regressionCoordinatesFormatted(regressionCoefficients.points[i][0], regressionCoefficients.points[i][1]) formatted.push(curr) } console.log('格式化后的数据:', formatted) return formatted; } return []; // 出错时返回空数组 }) .then((formattedData) => { setData(formattedData) // 不要在这里直接log data,因为setData是异步的,要验证的话可以用useEffect监听data变化 }) .catch(err => { console.error('请求错误:', err) }) }, [stock]) // 监听data变化,验证状态是否更新 useEffect(() => { console.log('data状态已更新:', data) }, [data]) return ( <div style={{width: '100%'}}> <FlexBetween mb='0.25rem' p='1rem 1rem' > <FlexBetween gap='2rem' color='lightgray'> 90-Day Summary </FlexBetween> <FlexBetween gap='2rem'> <FormControl variant='standard' sx={{ m: 1, minWidth: 120, justifyContent: 'space-between' }} > <InputLabel id='demo-simple-select-standard-label'>Stock</InputLabel> <Select labelId='demo-simple-select-standard-label' id='demo-simple-select-standard' value={stock} onChange={handleChange} label='Stock' sx={{ color: 'white', float: 'right', position: 'relative' }} > {menuItems} </Select> </FormControl> </FlexBetween> </FlexBetween> <ResponsiveContainer width='100%' height='100%'> <ComposedChart width={500} height={400} data={data} margin={{ top: 20, right: 80, bottom: 20, left: 20 }} > <Tooltip /> <XAxis dataKey='x' type='number' label={{ value: 'Index', position: 'insideBottomRight', offset: 0 }} /> <YAxis unit='USD' type='number' label={{ value: 'Price', angle: -90, position: 'insideLeft' }} /> <Scatter name='high' dataKey='high' fill='white' /> <Scatter name='low' dataKey='low' fill='purple' /> <Line dataKey='y' stroke='lightgrey' dot={false} activeDot={false} legendType='none' /> </ComposedChart> </ResponsiveContainer> </div> ) } export default Regression
关键修复点说明
- 变量作用域调整:把
formatted、regressionData移到useEffect内部,每次请求时重新初始化,避免数据累积污染。 - 修正错误判断:改用检查
chart.result是否存在且无error的逻辑,避免因为error类型判断错误导致代码不执行。 - 处理异步状态更新:移除
setData后的console.log(data),改用单独的useEffect监听data变化来验证状态更新。 - 数据安全处理:在循环里加了判空逻辑,避免API返回的部分数据为undefined导致报错;同时修正月份显示(月份是0-11,所以加1)。
- 可选优化:useMemo缓存options
如果想优化性能,可以用useMemo缓存请求配置,避免每次渲染重复创建对象:
const options = useMemo(() => ({ method: 'GET', url: `https://yfapi.net/v8/finance/chart/${stock}`, headers: { 'x-api-key': '<api key>' }, params: { range: '6mo', region: 'US', interval: '1d', ticker: stock } }), [stock])
关于useMemo的说明
你提到的useMemo在这里并不是必须的,它的作用是缓存复杂计算的结果,避免每次渲染都重复计算。你的问题根源是变量作用域和异步更新的问题,和useMemo无关。
内容的提问来源于stack exchange,提问作者Camila Daniels
相关产品推荐
相关产品推荐

