React - useEffect axios请求中设置的状态为空

Posted

技术标签:

【中文标题】React - useEffect axios请求中设置的状态为空【英文标题】:React - State set inside useEffect axios request is empty 【发布时间】:2021-09-26 12:41:41 【问题描述】:

嘿,

我有一个 PortfolioPage 组件(下面的代码)通过组件 useEffect 内的 axios 库向我的 api 调用 GET 请求。

请求之后 它使用 setPortfolio 挂钩设置投资组合属性。

然后,我将投资组合传递给 HoldingsChart 组件(添加的组件的代码)。 当我尝试使用在 HoldingsChart 组件中传递的投资组合道具时,它显示投资组合是一个空对象。

我相信这与 setState 的异步方式有关 但是另一个组件(AssetChart)确实得到了正确渲染,所以我找不到问题。 真的很想得到一些帮助。

PortfolioPage 组件代码:

const PortfolioPage = (match) =>
    const user = useContext(UserContext)
    const [portfolio, setPortfolio] = useState()
    const portfolioId = match.params.portfolioId
    useEffect(() => 
        // getPortfolio(user.token, portfolioId, setPortfolio)
        const getPortfolio = async () => 
            const domain = "http://127.0.0.1:8000/api"
            await axios.get(domain + `/portfolios/$portfolioId`,
                headers:
                    'Authorization': `Token $user.token`
                
            )
            .then((res) => 
                setPortfolio(res.data)
            )
        
        getPortfolio()
    ,[])

    return (
        <React.Fragment>
            <AssetChart records=portfolio.records />
            <HoldingsChart portfolio=portfolio/>
        </React.Fragment>
    )

HoldingsChart 组件代码:

const HoldingChart = (portfolio) =>
    const holdings = portfolio.holdings
    const totalValue = portfolio.total_value
    const [data, setData] = useState([])
    useEffect(() =>
        const holdingsPercentages = []
        holdings.forEach(holding => holdingsPercentages.push(value: holding.total_value / totalValue))
        setData(holdingsPercentages)
    ,[])

    const renderCustomizedLabel = (
        cx, cy, midAngle, innerRadius, outerRadius, percent, index,
    ) => 
        const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
        const x = cx +  radius * Math.cos(-midAngle * RADIAN);
        const y = cy + radius * Math.sin(-midAngle * RADIAN);
        return (
            <text x=x y=y fill="black" fontSize="10" textAnchor=x > cx ? 'start' : 'end' dominantBaseline="central">
                `$data[index].asset $(percent * 100).toFixed(0)%`
            </text>
        );
    ;
    return (
        <PieChart width=350 height=350>
            <Pie
                data=data
                cx=175
                cy=100
                labelLine=false
                label=renderCustomizedLabel
                innerRadius=40
                outerRadius=80
                fill="#8884d8"
                dataKey="value"
            >
                
                    data.map((entry, index) => <Cell key=`cell-$index` fill=COLORS[index % COLORS.length] />)
                
                <Tooltip/>
            </Pie>
        </PieChart>
    );
    

错误:

TypeError: Cannot read property 'forEach' of undefined
(anonymous function)
C:/Users/David/Desktop/Long-Term/longterm/src/components/assets-comps/HoldingsChart.js:15
  12 |    const [data, setData] = useState([])
  13 |    useEffect(() =>
  14 |        const holdingsPercentages = []
> 15 |        holdings.forEach(holding => holdingsPercentages.push(value: holding.total_value / totalValue))
     | ^  16 |        setData(holdingsPercentages)
  17 |    ,[])
  18 | 

谢谢

【问题讨论】:

如果你写console.log(portfolio) Insider HoldingChart 组件你看到了什么? @monesulhaque 它打印一个空对象 如果你这样做,你会看到什么? .then((res) => console.log(res); setPortfolio(res.data) ) @monesulhaque 它打印从 api 返回的对象,它还使用投资组合对象渲染 AssetChart。 如果您成功接收数据,那么可能存在一个问题,您的HoldingChart组件在您进行API调用之前呈现。所以请在holdings.forEach(holding =&gt; holdingsPercentages.push(value: holding.total_value / totalValue)) 之前检查一下是否为空。 【参考方案1】:

在循环holdings之前添加一个空检查

if(holdings && holdings.length)  
 holdings.forEach

holdings && holdings.length && holdings.forEach

holdings?.forEach 

原因:

您的持有图表组件甚至在收到 api 响应之前就已呈现。 (收到api响应时会重新渲染,所以添加null检查即可解决问题。

更简单更好的解决方案

添加一个变量 isResponseLoading 以在 api 调用正在进行时显示加载器并呈现您的 HoldingsChart 组件,仅当 api 响应可用时。

isResponseLoading && <Spinner />
!isResponseLoading && portfolio?.length && <HoldingsChart portfolio=portfolio/> 

【讨论】:

以上是关于React - useEffect axios请求中设置的状态为空的主要内容,如果未能解决你的问题,请参考以下文章

在 React Hooks useEffect cleanup 中取消 Axios REST 调用失败

Axios 没有使用 useEffect() (React Js) [重复]

为啥我的 axios 使用 React.useEffect 一遍又一遍地从 Rails 后端获取调用?

React useEffect 无限循环获取数据 axios

无法在 useEffect() 中从 AXIOS 获取数据

使用 axios 获取数据后 React useEffect 返回空数组