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

解决TS2339错误:DropdownContextTypeProps|null无DropdownToggle属性

解决React下拉菜单的TypeScript TS2339错误

问题原因

你定义的DropdownMenuContext类型是DropdownContextTypeProps | null,使用useContext获取上下文时,TypeScript无法确定返回值一定不为null,因此解构DropdownToggle时会触发TS2339错误——尽管实际运行时所有子组件都在DropdownMenuContext.Provider内部,上下文不会为null。

解决方案

推荐使用自定义Hook的方式,既解决TS类型问题,又能避免组件在Provider外部使用的潜在错误,具体如下:

1. 编写自定义Hook

在代码中添加一个专门获取下拉菜单上下文的Hook,确保上下文不为空:

// 在createContext之后添加
const useDropdownMenuContext = () => {
  const context = useContext(DropdownMenuContext);
  if (!context) {
    throw new Error('DropdownMenu.Button 必须在 DropdownMenu 组件内部使用');
  }
  return context;
};

2. 修改DropdownMenuButton组件

用自定义Hook替换原有的useContext调用:

const DropdownMenuButton = ({ children, icon }: DropdownMenuButtonProps) => {
    const { DropdownToggle } = useDropdownMenuContext();
    const DropdownButtonClick = () => DropdownToggle()

    return (
        <div className="dropdown-menu__button" onClick={DropdownButtonClick}>
            <span className="dropdown-menu__button-icon">{icon}</span>
            <span>{children}</span>
            <span className="dropdown-menu__button-toggle-icon">
                <Icon icon="down" />
            </span>
        </div>
    )
}

其他可选方案(不推荐)

  • 非空断言:直接在解构时添加!强制断言上下文不为null,但如果组件意外在Provider外部使用,会导致运行时错误:
    const { DropdownToggle } = useContext(DropdownMenuContext)!;
    
  • 设置上下文默认值:给createContext传入符合类型的默认值,去掉| null类型,但默认的空函数在Provider外部使用时无实际作用:
    const DropdownMenuContext = createContext<DropdownContextTypeProps>({
        dropdownOpen: false,
        DropdownToggle: () => {}
    });
    

修改后的完整代码

import './DropdownMenu.scss'
import { NavLink } from 'react-router-dom'
import { createContext, ReactNode, useContext, useState } from 'react'
import { Icon } from '@/Components/Ui/Icon'

type DropdownMenuProps = {
    children: ReactNode
}

type DropdownMenuButtonProps = {
    children: ReactNode
    icon: ReactNode
}

type DropdownMenuItemsProps = {
    children: ReactNode
}

type DropdownMenuItemProps = {
    children: ReactNode
    url: string
}

type DropdownContextTypeProps = {
    dropdownOpen: boolean
    DropdownToggle: () => void
}

const DropdownMenuContext = createContext<DropdownContextTypeProps | null>(null)

// 自定义Hook确保上下文不为空
const useDropdownMenuContext = () => {
  const context = useContext(DropdownMenuContext);
  if (!context) {
    throw new Error('DropdownMenu.Button 必须在 DropdownMenu 组件内部使用');
  }
  return context;
};

export const DropdownMenu = ({ children }: DropdownMenuProps) => {
    const [dropdownOpen, setDropdownOpen] = useState<boolean>(false)
    const DropdownToggle = () => setDropdownOpen(!dropdownOpen)
    const DropdownContextData = {
        dropdownOpen,
        DropdownToggle,
    }
    return (
        <DropdownMenuContext.Provider value={DropdownContextData}>
            <div className={`dropdown-menu ${dropdownOpen ? 'open' : ''}`}>
                {children}
            </div>
        </DropdownMenuContext.Provider>
    )
}

const DropdownMenuButton = ({ children, icon }: DropdownMenuButtonProps) => {
    const { DropdownToggle } = useDropdownMenuContext();
    const DropdownButtonClick = () => DropdownToggle()

    return (
        <div className="dropdown-menu__button" onClick={DropdownButtonClick}>
            <span className="dropdown-menu__button-icon">{icon}</span>
            <span>{children}</span>
            <span className="dropdown-menu__button-toggle-icon">
                <Icon icon="down" />
            </span>
        </div>
    )
}

const DropdownMenuItems = ({ children }: DropdownMenuItemsProps) => {
    return (
        <div className="dropdown-menu__items">
            <div className="dropdown-menu__items-content">{children}</div>
        </div>
    )
}

const DropdownMenuItem = ({ children, url }: DropdownMenuItemProps) => {
    return (
        <NavLink to={url} className="dropdown-menu__item">
            <span className="dropdown-menu__item-icon">
                <Icon icon="circle" />
            </span>
            <span>{children}</span>
        </NavLink>
    )
}

DropdownMenu.Button = DropdownMenuButton
DropdownMenu.Items = DropdownMenuItems
DropdownMenu.Item = DropdownMenuItem

内容的提问来源于stack exchange,提问作者Selçuk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:02:49