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

时间:2021-07-18 15:27:43

标签: reactjs axios use-effect setstate

嘿,

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

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

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

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

作品集页面组件代码:

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 | 

谢谢

1 个答案:

答案 0 :(得分:1)

在遍历 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}/> }