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

等待useQuery返回数据后写入React状态的报错排查

解决React中Apollo Client查询数据存入State后渲染报错的问题

问题原因

你添加React State存储查询数据后出现TypeError: Cannot read properties of null (reading 'firstName'),核心原因是:

  • 初始State设为null,虽然有loading判断,但loading结束后,useEffect是在组件渲染完成后才执行的——这意味着第一次渲染时participant还是初始的null,传给ProfileGeneral组件后,子组件直接访问participant.firstName就会触发空值报错。
  • 原代码直接从data同步解构participant,loading结束后data已就绪,所以不会有问题;但通过useEffect异步更新State,就出现了渲染时机不匹配的问题。

解决方案

方案1:渲染前校验State值

在渲染子组件前,先确认participant有值,避免传递空值:

return (
    <div>
       {/* 只有participant存在时才渲染子组件 */}
       {participant ? <ProfileGeneral participant={participant} /> : <div>Loading...</div>}
    </div>
)

方案2:用useQuery的onSuccess回调同步更新State

不需要依赖useEffect,直接利用Apollo useQuery的onSuccess回调,查询成功后立即更新State,保证数据同步:

import { useQuery } from '@apollo/client';
import { SINGLE_PARTICIPANT_QUERY } from 'queries/participantQueries';
import { ProfileGeneral } from './ProfileGeneral';
import { useState } from 'react';

const ProfilePage = ({ id }) => {
    const [participant, setParticipant] = useState(null);
    const { loading, error } = useQuery(SINGLE_PARTICIPANT_QUERY, {
        variables: { id },
        onSuccess: (data) => {
            data?.participant && setParticipant(data.participant);
        }
    });

    if (loading) {
        return <div>Loading</div>;
    }
    if (error) {
        return <div>{error.message}</div>;
    }
    if (!participant) {
        return <div>No participant data found</div>;
    }

    return (
        <div>
           <ProfileGeneral participant={participant} />
        </div>
    )
}

方案3:子组件内部做空值处理

在ProfileGeneral组件内部对接收的participant做校验,从根源避免空值访问:

// ProfileGeneral.jsx
const ProfileGeneral = ({ participant }) => {
    if (!participant) {
        return <div>Loading participant info...</div>;
    }
    return (
        <div>
            <p>First Name: {participant.firstName}</p>
            {/* 其他表单内容 */}
        </div>
    )
}

额外优化:清理useEffect的无效依赖

如果一定要保留useEffect,注意不要把participant放进依赖数组(会触发无限循环),只保留data即可:

useEffect(() => {
    if (data && data.participant) {
        setParticipant(data.participant);
    }
}, [data]); // 移除participant依赖

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 02:50:21