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

Next.js+TypeScript自定义Context无法读取,触发异常错误

问题:Next.js + TypeScript自定义Context无法读取,触发"Context doesn't exists"异常

在Next.js与TypeScript开发中,尝试为表单添加自定义错误通知时,创建的自定义Context无法被读取,触发Context doesn't exists的自定义异常。

自定义Context实现代码

"use client"
import React, { useContext } from 'react'
import { Notification } from "../components/Notification"
import { AlertColor } from '@mui/material';

type ContextProperties = {
    getError: (msg: string) => void,
}


export const NotificationProvider: React.FC<{ children: JSX.Element }> = ({ children }) => {

    //Con estos tres modificamos los estados del tipo de componente Notification.
    const [open, setOpen] = React.useState(false);
    const [message, setMessage] = React.useState('');
    const [severity, setSeverity] = React.useState<AlertColor | undefined>(undefined);

    //Se maneja por fuera del return, si fuera ahi dentro seria dentro de la aplicación y no queremos eso.
    //maneja cuando quiero cerrar la notificación.
    const handleClose = () => {
        setOpen(false);
    }

    const getError = (msg: string) => {

        setSeverity("error");
        setOpen(true);
        setMessage(msg);

    }

    const value = {
        getError
    }

    //.provider es una propiedad del creador de context de react. Lo que estamos creando es un proveedor.
    //para ello necesitamos crear un contexto, que nos permite llamar el atributo/clase provider, que permite inyectarlo en toda la aplicacion.

    //El componente notification no engloba nada está al mismo nivel que el children.
    return (
        <NotificationContext.Provider value={value}>
            <Notification handleClose={handleClose} open={open} severity={severity} message={message} />
            {children}
        </NotificationContext.Provider>
    )
}

const NotificationContext = React.createContext<ContextProperties | null>(null);

export const useNotification = () => {
    const context = useContext(NotificationContext);
    if (!context) throw new Error('Context doesn\'t exists')
    return context
}

Context使用代码

"use client"

import { Button, Container, Grid } from '@mui/material'
import Typography from '@mui/material/Typography'
import Image from "next/image"
import Link from 'next/link'

import { useNotification } from './context/notification.context'


export default function Home() {


  const { getError } = useNotification();

  const handleClick = () => {
    getError('Hello world');
  }

  return (
    <>
      <Container component="main" maxWidth="lg" >

        <Grid
          container
          alignItems="center"
          justifyContent="center"
          direction="column"
          sx={{ minHeight: "90vh" }}
          spacing={5}
        >
          <Grid item>
            <Image
              className="border-slate pb-8"
              src="/images/AppLogo.png"
              width={300}
              height={300}
              alt="AppLogo"
              priority={true} />
          </Grid>

          <Grid item>
            <Typography variant='h1'>Welcome to SharpSight</Typography>
          </Grid>

          <Grid item >
            <Typography variant="h4" fontWeight="light">A web application for price comparison</Typography>
          </Grid>

          <Grid item >
            <Link href="/register">
              <Button variant="contained" type="button">Get Started</Button>
            </Link>
          </Grid>

          <Grid item>
            <Button variant='contained' type="button" onClick={handleClick}>Notification Test</Button>
          </Grid>
        </Grid>

      </Container>
    </>
  )
}
解决方案

1. 确保使用Context的组件被Provider包裹

异常的核心原因是调用useNotification的组件(比如Home组件)没有被NotificationProvider包裹,导致useContext获取到null,触发异常。需要在应用的根组件中用Provider包裹所有需要使用该Context的组件:

若使用App Router(layout.tsx)

// app/layout.tsx
"use client"
import { NotificationProvider } from './context/notification.context'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <NotificationProvider>{children}</NotificationProvider>
      </body>
    </html>
  )
}

若使用Pages Router(_app.tsx)

// pages/_app.tsx
"use client"
import { NotificationProvider } from '../context/notification.context'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps }: AppProps) {
  return (
    <NotificationProvider>
      <Component {...pageProps} />
    </NotificationProvider>
  )
}

2. 修正Provider的children类型

当前NotificationProvider的children类型定义为JSX.Element,但React组件的children支持多个元素、字符串、null等,应该改为React.ReactNode:

export const NotificationProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  // 原有逻辑不变
}

3. 调整Context创建顺序(可选但更规范)

将NotificationContext的创建移到NotificationProvider之前,逻辑上更清晰,避免变量提升带来的潜在问题:

"use client"
import React, { useContext } from 'react'
import { Notification } from "../components/Notification"
import { AlertColor } from '@mui/material';

type ContextProperties = {
    getError: (msg: string) => void,
}

// 先创建Context
const NotificationContext = React.createContext<ContextProperties | null>(null);

export const NotificationProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  // 原有逻辑不变
}

export const useNotification = () => {
  const context = useContext(NotificationContext);
  if (!context) throw new Error('Context doesn\'t exists')
  return context
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 22:44:56