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

React中如何确保JSX仅在state变量填充JSON响应后渲染?

问题解决思路与代码修复

核心问题分析

  1. 误用useMemo处理API请求:useMemo是用来缓存计算结果的,不是处理API请求这类副作用的正确工具,应该用useEffect。
  2. 初始状态设置错误:你把tutorDetails初始化为数组[],但API返回的是对象,直接访问tutorDetails.subjects会因为数组没有该属性报错。
  3. 未处理异步加载状态:数据请求是异步的,JSX会先执行,此时tutorDetails还没被赋值,直接访问内部属性会抛出错误。

解决方案

1. 替换useMemo为useEffect

用useEffect处理API请求,同时把props.tutorID加入依赖数组,确保导师ID变化时重新请求数据。

2. 修正初始状态

将tutorDetails初始值设为null,方便判断数据是否加载完成。

3. 条件渲染JSX

在渲染tutorDetails相关内容前,先判断数据是否存在,避免报错;也可以添加加载状态提示。

修复后的完整代码

import React, { useState, useEffect } from "react";
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';
import Slide from '@mui/material/Slide';
import axios from 'axios';

function TutorCard({ tutor_id, first_name, last_name, grade, subject }) {
    return (
        <div className="md:flex bg-white shadow text-gray-800 my-4 py-4 px-10 rounded-md items-center justify-between hover:bg-gray-300">
            <p className="font text-md" style={{ color: 'black', textAlign: "center" }}>{tutor_id}</p>
            <p className="font text-md" style={{ color: 'black', textAlign: "center" }}>{first_name}</p>
            <p className="font text-md" style={{ color: 'black', textAlign: "center" }}>{last_name}</p>
            <p style={{ color: 'black', textAlign: "center" }}>{grade}</p>
            <p style={{ color: 'black', textAlign: "center" }}>{subject}</p>
            <AlertDialogSlide tutorID={tutor_id} firstName={first_name} lastName={last_name} grade={grade} subject={subject} />
        </div>
    )
}

const Transition = React.forwardRef(function Transition(props, ref) {
    return <Slide direction="up" ref={ref} {...props} />;
});

function AlertDialogSlide(props) {
    const [open, setOpen] = React.useState(false);
    const [tutorDetails, setTutorDetails] = useState(null);
    const [isLoading, setIsLoading] = useState(false);

    const handleClickOpen = () => {
        setOpen(true);
    };

    const handleClose = () => {
        setOpen(false);
    };

    // 用useEffect替代useMemo处理API请求
    useEffect(() => {
        // 弹窗打开时才请求数据,避免不必要的请求
        if (!open) return;
        
        const retrieveData = async () => {
            setIsLoading(true);
            try {
                const resp = await axios.get('API call' + props.tutorID);
                setTutorDetails(resp.data);
            } catch (error) {
                console.error('获取导师详情失败:', error);
            } finally {
                setIsLoading(false);
            }
        }

        retrieveData();
    }, [open, props.tutorID]);

    return (
        <div>
            <Button variant="outlined" onClick={handleClickOpen}>
                {'>'}
            </Button>
            <Dialog
                open={open}
                TransitionComponent={Transition}
                keepMounted
                onClose={handleClose}
                aria-describedby="alert-dialog-slide-description"
                fullWidth={true}
            >
                <DialogTitle>{props.firstName + " " + props.lastName}</DialogTitle>
                <DialogContent>
                    <DialogContentText>
                        <strong>Level: </strong> {props.grade} <br />
                        <strong>Subject: </strong> {props.subject} <br />
                        {/* 条件渲染:先判断加载状态和数据是否存在 */}
                        {isLoading ? (
                            <span>加载中...</span>
                        ) : tutorDetails ? (
                            <>
                                <strong>Alternative Subject: </strong> {tutorDetails.subjects?.[0] || '无'} <br />
                                {/* 若要渲染所有备选科目,可取消下面注释 */}
                                {/* <div>
                                    {tutorDetails.subjects?.map((sub, index) => (
                                        <div key={index}>
                                            <strong>Alternative Subject {index + 1}: </strong> {sub} <br />
                                        </div>
                                    ))}
                                </div> */}
                            </>
                        ) : (
                            <span>暂无更多信息</span>
                        )}
                    </DialogContentText>
                </DialogContent>
                <DialogActions>
                    <Button onClick={handleClose}>Close</Button>
                </DialogActions>
            </Dialog>
        </div>
    );
}
export default TutorCard;

关键优化点说明

  • useEffect处理异步请求:符合React副作用处理规范,依赖数组确保请求时机正确。
  • 加载状态提示:提升用户体验,避免数据未加载时的空白或错误。
  • 可选链操作符?.:即使tutorDetails.subjects不存在也不会报错,增强代码健壮性。
  • 条件渲染:确保只有数据加载完成后才渲染对应内容,解决JSX提前执行导致的无数据问题。

内容的提问来源于stack exchange,提问作者BaMdan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 02:02:02