Типизация контекста и пользовательских хуков
Создавайте типизированный контекст со значениями по умолчанию и пишите пользовательские хуки с явными возвращаемыми типами.
«Типизация контекста и пользовательских хуков» — бесплатный урок React Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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 будет проверять допустимость типов действий в редьюсере.
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 в пользовательских хуках. Применяйте обобщённые типы, 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 размеченными объединениями действий, чтобы выявлять недопустимые отправки во время компиляции.
Часто задаваемые вопросы
Урок «Типизация контекста и пользовательских хуков» бесплатный?
Да — полный текст урока «Типизация контекста и пользовательских хуков» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс React Academy, подпишись на CoddyKit PRO. Курс React Academy содержит 4 уроков всего.
Чему я научусь в уроке «Типизация контекста и пользовательских хуков»?
Создавайте типизированный контекст со значениями по умолчанию и пишите пользовательские хуки с явными возвращаемыми типами. Ты практикуешь React Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать React Academy?
Предыдущий опыт не требуется. React Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Типизация контекста и пользовательских хуков»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке React Academy?
Да. Каждый урок React Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Типизация свойств и возвращаемых типов компонентов
- Типизация событий и ссылок в TypeScript
- Обобщённые компоненты и вспомогательные типы
- Типизация контекста и пользовательских хуков