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

为何TypeScript无法识别Context包装器提供的值?

解决React Context的TypeScript解构错误

错误原因

你定义的NavContext类型是NavContextType | null,当使用useContext获取值时,TypeScript无法确定返回的是NavContextType还是null,直接解构null会触发属性不存在的错误。

解决方案

方案1:给Context提供符合类型的默认值

修改Context的创建代码,去掉null类型,直接提供一个符合NavContextType的默认值:

type NavContextType = {
    isDrawerOpen: boolean;
    setIsDrawerOpen: React.Dispatch<React.SetStateAction<boolean>>;
};

// 提供默认值,类型不再包含null
export const NavContext = createContext<NavContextType>({
    isDrawerOpen: false,
    setIsDrawerOpen: () => {}, // 空函数作为默认dispatch,避免运行时错误
});

const NavBar: ReactFC<{ children?: React.ReactNode }> = ({ children }) => {
    const [isDrawerOpen, setIsDrawerOpen] = useState(false);

    return (
        <NavContext.Provider value={{ isDrawerOpen, setIsDrawerOpen }}>
            <ToolBar />
            {children}
        </NavContext.Provider>
    );
};

这样useContext(NavContext)返回的类型就是确定的NavContextType,解构时不会报错。

方案2:使用自定义Hook封装Context(推荐)

创建一个自定义Hook,在内部做null检查,确保组件必须在Provider范围内使用:

// 在NavBar.tsx中添加自定义Hook
export const useNavContext = () => {
    const context = useContext(NavContext);
    if (!context) {
        throw new Error("useNavContext必须在NavContext.Provider内部使用");
    }
    return context;
};

然后在ToolBar中使用这个Hook:

import { useNavContext } from "./NavBar";

const ToolBar = () => {
    // 直接解构,无需担心null
    const { isDrawerOpen, setIsDrawerOpen } = useNavContext();
    return (
        // 组件内容
    );
};

这种方式既保证了类型安全,又能在错误使用时给出清晰的报错信息。

方案3:非空断言(不推荐)

如果能确保ToolBar永远在NavContext.Provider内部渲染,可以用非空断言!告诉TypeScript值不会为null:

const { isDrawerOpen, setIsDrawerOpen } = useContext(NavContext)!;

但这种方式有风险,如果后续代码不小心把ToolBar移到Provider外,会触发运行时错误。

额外注意

原NavBar组件的类型定义有语法错误,const NavBar: :ReactFC多了一个冒号,应该修正为const NavBar: ReactFC<{ children?: React.ReactNode }>。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 01:10:15