假设我有一个基本设置来检查用户是否登录:
import { createContext } from "react";
const UserContext = createContext<string | null>(null)
export default UserContext
在我的 App.tsx 中,我想创建一个 useState 挂钩,以便我可以管理整个应用程序中的上下文状态:
//Context
const [context, setContext] = useState<string | null>(null)
<UserContext.Provider value={{context, setContext}}>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/login" element={<Login/>}/>
<Route path="/register" element={<Register/>}/>
<Route path="/admin" element={<Admin/>}></Route>
</Routes>
</UserContext.Provider>
据我所知,我只需要登录用户的名称,或者如果我想拒绝访问某些页面,则将上下文的状态设置为空。现在的问题是打字稿在这里对我大喊大叫,特别是在 Provider 的值上:
Type '{ context: string | null; setContext:
React.Dispatch<React.SetStateAction<string | null>>; }' is not assignable to type 'string'.ts(2322)
我可以按如下方式强制转换值:
value={{context, setContext} as any}
但这似乎不是一个特别优雅的“打字”解决方案。
如有任何帮助,我们将不胜感激!
根据亚历克斯的回答,我想出了一个适合我的调整:
type UserContextType = {
context: string | null,
setContext: React.Dispatch<React.SetStateAction<string | null>>
}
const iUserContextState = {
context: null,
setContext: () => {}
}
const UserContext = createContext<UserContextType>(iUserContextState)
export default UserContext
然后在 App.tsx 中:
//Context
const [context, setContext] = useState<string | null>(null)
<UserContext.Provider value={{context, setContext}}>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/login" element={<Login/>}/>
<Route path="/register" element={<Register/>}/>
<Route path="/admin" element={<Admin/>}></Route>
</Routes>
</UserContext.Provider>
您输入的上下文为:
string | null
但是你为该上下文提供了一个(大约)类型的值:
{ context: string | null, setContext: (newString) => void }
因此,如果您希望在上下文中使用状态设置器,那么它需要成为上下文类型的一部分:
const UserContext = createContext<{
context: string | null,
setContext: (newValue) => void
}>({
context: null,
setContext: () => undefined
})
如果你想保留
useState
的数组作为返回值,这是一个正确的 TypeScript 解决方案:
import React, { FC, createContext, useContext, useState } from 'react';
// Change shape of this object to your/app needs
const initialAppState = {
userName: "",
};
export type AppState = typeof initialAppState;
type ContextType = [AppState, React.Dispatch<React.SetStateAction<AppState>>];
const AppStateContext = createContext<ContextType>([] as unknown as ContextType);
type Props = {
appState?: AppState;
};
/**
* Provider to pass state to the React application
*/
export const AppStateProvider: FC<React.PropsWithChildren<Props>> = (props) => {
const { appState = null } = props;
const useStateRetValue = useState<AppState>(appState == null ? initialAppState : appState);
return <AppStateContext.Provider value={useStateRetValue}>{props.children}</AppStateContext.Provider>;
};
AppStateProvider.defaultProps = {
appState: undefined,
};
/**
* Hook to access application state
*/
export const useAppStateContext = (): ContextType => useContext<ContextType>(AppStateContext);
提供商使用情况:
<AppStateProvider>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/login" element={<Login/>}/>
<Route path="/register" element={<Register/>}/>
<Route path="/admin" element={<Admin/>}></Route>
</Routes>
</AppStateProvider>
挂钩用法:
const [appState, setAppState] = useAppStateContext();
console.log(appState.userName);
useEffect(() => {
setAppState({ userName: "John Doe" });
}, [setAppState]);
// ...or...
const [, setAppState] = useAppStateContext();
// ...or...
const [appState] = useAppStateContext();