为 Context 与自定义钩子定义类型
使用默认值创建类型化 Context,并为自定义钩子编写显式返回类型。
为 Context 与自定义钩子定义类型 是 CoddyKit 上的免费 React Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Academy 课程共包含 4 节课。
使用 createContext 创建类型化上下文
将上下文类型和默认值传递给 createContext。当上下文必须在提供者内部使用时,请将 null 作为默认值,并通过守卫处理它。
interface AuthContextType {
user: User | null;
login: (credentials: Credentials) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthContextType | null>(null);守卫钩子模式
创建一个读取上下文的自定义钩子,并在钩子于提供者外部使用时抛出错误。这样可以避免在每个调用位置检查 null。
export function useAuth(): AuthContextType {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
return ctx;
}为提供者指定类型
提供者组件会包装子元素并提供上下文值。请明确指定值对象的类型,让 TypeScript 验证它是否与上下文类型匹配。
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const login = async (creds: Credentials) => {
const u = await apiLogin(creds);
setUser(u);
};
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}带默认值的上下文
当存在合理的默认值(例如主题)时,请直接将其传递给 createContext,这样上下文永远不会为 null,也不需要守卫钩子。
interface ThemeContextType { theme: 'light' | 'dark'; toggle: () => void; }
const ThemeContext = createContext<ThemeContextType>({
theme: 'light',
toggle: () => {},
});
export const useTheme = () => useContext(ThemeContext);为自定义钩子的返回值指定类型
请明确标注自定义钩子的返回类型,或者让 TypeScript 自动推断。明确的类型可以充当文档,并防止结构意外发生变化。
interface UseCounterReturn {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
function useCounter(initial = 0): UseCounterReturn {
const [count, setCount] = useState(initial);
return {
count,
increment: () => setCount(c => c + 1),
decrement: () => setCount(c => c - 1),
reset: () => setCount(initial),
};
}元组返回类型
返回一对值(如 useState)的钩子使用元组。请使用 as const 或明确的元组类型,以便正确推断解构结果。
function useToggle(initial = false): [boolean, () => void] {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue(v => !v), []);
return [value, toggle];
}
// Destructuring correctly infers types:
const [isOpen, toggleOpen] = useToggle();泛型自定义钩子
自定义钩子也可以使用泛型。泛型 useLocalStorage 钩子能够以完整的类型安全性存储任意可序列化类型。
function useLocalStorage<T>(key: string, initialValue: T): [T, (val: T) => void] {
const [stored, setStored] = useState<T>(() => {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch { return initialValue; }
});
const setValue = (val: T) => {
setStored(val);
localStorage.setItem(key, JSON.stringify(val));
};
return [stored, setValue];
}为 useReducer 指定类型
将操作类型定义为可辨识联合,并将状态结构定义为接口。TypeScript 会在 reducer 中强制执行有效的操作类型。
interface State { count: number; status: 'idle' | 'loading'; }
type Action =
| { type: 'INCREMENT' }
| { type: 'SET_STATUS'; payload: State['status'] };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'INCREMENT': return { ...state, count: state.count + 1 };
case 'SET_STATUS': return { ...state, status: action.payload };
}
}
const [state, dispatch] = useReducer(reducer, { count: 0, status: 'idle' });为 useCallback 与 useMemo 指定类型
TypeScript 会根据工厂函数为 useCallback 和 useMemo 推断类型。只有在推断失败时才需要明确标注。
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
onChange(e.target.value);
},
[onChange]
);
const sorted = useMemo(
() => [...items].sort((a, b) => a.name.localeCompare(b.name)),
[items]
); // inferred as typeof items带类型的上下文选择器模式
类型化的选择器钩子接收一个选择器函数,并只返回所需的数据切片,同时保留完整的类型推断。
function useAuthSelector<T>(selector: (ctx: AuthContextType) => T): T {
const ctx = useAuth();
return selector(ctx);
}
// Usage — TypeScript infers User | null:
const user = useAuthSelector(ctx => ctx.user);避免在钩子中使用 any
请不要在自定义钩子中使用 any。请使用泛型、结合类型守卫的 unknown,或具体接口,以保持类型链完整。
// Bad:
function useFetch(url: string): { data: any; loading: boolean } { ... }
// Good:
function useFetch<T>(url: string): { data: T | null; loading: boolean } {
const [data, setData] = useState<T | null>(null);
// ...
return { data, loading };
}快速检查
为 React 上下文指定类型时,守卫钩子模式的用途是什么?
回顾
使用明确的接口和检查 null 的钩子为上下文指定类型。使用接口或元组类型标注自定义钩子的返回类型,为可复用钩子使用泛型,并使用可辨识联合操作为 useReducer 指定类型,从而在编译时发现无效的分发操作。
常见问题解答
「为 Context 与自定义钩子定义类型」课时是免费的吗?
是的 — 「为 Context 与自定义钩子定义类型」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Academy 课程共包含 4 节课。
「为 Context 与自定义钩子定义类型」这节课中我会学到什么?
使用默认值创建类型化 Context,并为自定义钩子编写显式返回类型。 你通过在浏览器中直接运行的动手代码来练习 React Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 React Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 React Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「为 Context 与自定义钩子定义类型」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 React Academy 课中编写并运行代码吗?
能。每节 React Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 为属性与组件返回值定义类型
- 在 TypeScript 中为事件与引用定义类型
- 泛型组件与实用工具类型
- 为 Context 与自定义钩子定义类型