React Context中如何设置非空默认setState函数?规避可选链操作
解决React Context + TypeScript 可选链重复使用问题
方案1:修正Context类型,给setCurrentUser一个安全兜底默认值
createContext的默认值仅在组件未被UserProvider包裹时才会生效,我们可以利用这一点,让TypeScript认定setCurrentUser永远是一个有效函数,同时给它一个不会被实际调用的兜底空函数:
import { FC, createContext, useState, Dispatch, SetStateAction, PropsWithChildren } from "react" // 修改类型,移除setCurrentUser的null可能性 type UserContextType = { currentUser: UserType | null setCurrentUser: Dispatch<SetStateAction<UserType | null>> } export type UserType = { username: string } | null // 给setCurrentUser传一个空函数作为兜底默认值 export const UserContext = createContext<UserContextType>({ currentUser: null, setCurrentUser: () => {} }) export const UserProvider: FC<PropsWithChildren> = ({ children }) => { const [currentUser, setCurrentUser] = useState<UserType>({ username: "John" }) return <UserContext.Provider value={{ currentUser, setCurrentUser }}>{children}</UserContext.Provider> }
使用时setCurrentUser无需可选链,currentUser可通过条件判断做类型窄化:
// TestPage.tsx import { useContext } from "react" import { UserContext } from "../../contexts/UserContext" import { Button, Typography } from "@mui/material" export default function TestPage() { const { currentUser, setCurrentUser } = useContext(UserContext) return ( <> <Button onClick={() => setCurrentUser({ username: "Doe" })}>Change Name</Button> {/* 条件判断后TypeScript会自动窄化currentUser类型 */} {currentUser && <Typography>{currentUser.username}</Typography>} </> ) }
方案2:自定义useUserContext钩子,强制组件在Provider内使用
通过自定义钩子,确保组件只有在UserProvider包裹下才能使用Context,同时让TypeScript自动推断出Context值不会为null:
// 在UserContext.ts中新增自定义钩子 export const useUserContext = () => { const context = useContext(UserContext) if (!context) { throw new Error("useUserContext 必须在 UserProvider 内部使用") } return context }
使用时替换useContext为这个自定义钩子即可:
// TestPage.tsx import { useUserContext } from "../../contexts/UserContext" import { Button, Typography } from "@mui/material" export default function TestPage() { const { currentUser, setCurrentUser } = useUserContext() return ( <> <Button onClick={() => setCurrentUser({ username: "Doe" })}>Change Name</Button> {currentUser && <Typography>{currentUser.username}</Typography>} </> ) }
方案3:让currentUser始终非null(业务场景允许时)
如果你的业务逻辑中currentUser永远不会为null(比如用户必须登录才能进入应用),可以直接移除类型中的null可能性:
// UserContext.ts import { FC, createContext, useState, Dispatch, SetStateAction, PropsWithChildren } from "react" export type UserType = { username: string } // 移除| null type UserContextType = { currentUser: UserType setCurrentUser: Dispatch<SetStateAction<UserType>> } export const UserContext = createContext<UserContextType>({ currentUser: { username: "" }, // 兜底默认值 setCurrentUser: () => {} }) export const UserProvider: FC<PropsWithChildren> = ({ children }) => { const [currentUser, setCurrentUser] = useState<UserType>({ username: "John" }) return <UserContext.Provider value={{ currentUser, setCurrentUser }}>{children}</UserContext.Provider> }
此时使用时完全不需要可选链:
// TestPage.tsx import { useUserContext } from "../../contexts/UserContext" import { Button, Typography } from "@mui/material" export default function TestPage() { const { currentUser, setCurrentUser } = useUserContext() return ( <> <Button onClick={() => setCurrentUser({ username: "Doe" })}>Change Name</Button> <Typography>{currentUser.username}</Typography> </> ) }
内容的提问来源于stack exchange,提问作者lowcrawler
相关产品推荐
相关产品推荐

