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
相关产品推荐
相关产品推荐

