
本文详解如何在 React Query 中安全使用来自 localStorage 的用户数据,避免因 userData 为 null 导致的 TypeError: Cannot read properties of null (reading 'id'),重点修复查询启用条件(enabled)的空值判断逻辑。
本文详解如何在 react query 中安全使用来自 localstorage 的用户数据,避免因 `userdata` 为 null 导致的 `typeerror: cannot read properties of null (reading 'id')`,重点修复查询启用条件(`enabled`)的空值判断逻辑。
在使用 useLocalStorage("userData", null) 初始化用户状态时,userData 在组件首次渲染或 localStorage 为空时可能为 null。此时若直接访问 userData.id(如 ["user-parcels", userData?.id] 中的依赖键,或 userData.type === "biker" 中的属性读取),就会触发运行时错误——即使后续数据加载完成、应用表现正常,React 的严格模式或服务端渲染(SSR)场景下仍会抛出 TypeError。
根本原因在于:enabled 选项当前写为 userData.id.length > 0,该表达式隐含了对 userData.id 的强制访问,而 userData 本身可能为 null 或 undefined,导致 .id 访问失败。React Query 的 enabled 必须是一个纯布尔值,且其计算过程不能引发副作用或报错。
✅ 正确做法是使用可选链 + 逻辑非双重取反(!!userData?.id)确保安全判空:
本文档主要讲述的是React Native For Android 源码编译;希望对大家会有帮助;感兴趣的朋友可以过来看看
// useGetParcels.tsx(修正后)
import { getBikerParcels } from "@/models/bikerModels";
import { getSenderParcels } from "@/models/senderModels";
import { User } from "@/types/dataTypes";
import { useQuery } from "@tanstack/react-query";
const useGetParcels = (userData: User | null) => {
return useQuery(
["user-parcels", userData?.id], // 安全:?.id 返回 undefined 而非报错
async () => {
if (!userData?.id || !userData.type) {
throw new Error("Invalid user data: missing id or type");
}
let res;
if (userData.type === "biker") {
res = await getBikerParcels(userData.id);
} else if (userData.type === "sender") {
res = await getSenderParcels(userData.id);
} else {
throw new Error(`Unsupported user type: ${userData.type}`);
}
return res.data.parcels;
},
{
staleTime: 1000 * 60 * 100, // 10 分钟
enabled: !!userData?.id, // ✅ 关键修复:安全转布尔值,不触发属性访问异常
retry: 1,
}
);
};
export default useGetParcels;
同时,建议同步更新 Page.tsx 中的类型提示,明确 userData 可为空:
// Page.tsx(增强健壮性)
import React from "react";
import SenderTable from "./senderTable";
import useLocalStorage from "@/hooks/useLocalStorage";
import BikerTable from "./bikerTable";
import useGetParcels from "@/hooks/queries/useGetParcels";
import Loading from "@/components/Loading";
import Nav from "@/components/Nav";
export default function Dashboard() {
const [userData] = useLocalStorage<user null>("userData", null); // 显式泛型
const { data: parcels, isLoading, error } = useGetParcels(userData);
if (isLoading) return <loading></loading>;
if (error) return <div classname="text-red-500">加载失败:{error.message}</div>;
if (!userData) return <div>请先登录</div>; // 明确兜底
return (
{userData.type === "sender" && <nav></nav>}
{userData.type === "sender" ? (
<sendertable parcels="{parcels}"></sendertable>
) : (
<bikertable parcels="{parcels}"></bikertable>
)}
>
);
}</user>
⚠️ 注意事项:
- 永远不要在 enabled、查询键(queryKey)或查询函数中直接解构或访问可能为 null 的对象属性,优先用 ?. 和 ??;
- useQuery 的 enabled: false 会跳过请求,但不会清除已有缓存;如需彻底重置,请配合 queryClient.removeQueries() 或 invalidateQueries;
- 若 userData 来自异步初始化(如 Auth Context),建议结合 suspense: true 或 isSuccess 状态做更精细的 UI 控制;
- 生产环境建议添加 onError 回调统一处理认证失效等边界情况。
通过这一微小但关键的调整,你既消除了潜在崩溃风险,又保留了 React Query 的自动依赖追踪与缓存能力,真正实现「数据驱动、安全可靠」的状态管理。










