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

React - State set inside useEffect axios request is empty

嘿嘿,

我有一个 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 | 

谢谢

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

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

holdings && holdings.length && holdings.forEach

holdings?.forEach 

原因:

您的等待图表组件甚至在收到 api 响应之前就已呈现。 (当收到api响应时会再次重新渲染,所以添加空检查将解决问题。

更简单更好的解决方案

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

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