Hooks төрөлжүүлэх
React hooks — useState, useRef, useReducer зэрэг — TypeScript-тэй маш сайн хослодог. Ихэнх тохиолдолд TypeScript өөрөө төрлийг таньдаг, гэхдээ зарим нөхцөлд тодорхой заавал зааж өгөх шаардлагатай. Энэ хичээлд хамгийн түгээмэл hooks-ын TypeScript хэрэглээг практикаар судална.
useState төрөлжүүлэх
Энгийн утгуудад TypeScript ихэвчлэн өөрөө таньдаг тул нэмэлт зүйл бичихгүй байж болно:
// TypeScript автоматаар string гэж таньдаг
const [name, setName] = useState("Болд");
// TypeScript автоматаар number гэж таньдаг
const [count, setCount] = useState(0);
// TypeScript автоматаар boolean гэж таньдаг
const [isOpen, setIsOpen] = useState(false);
Харин эхний утга нь null эсвэл undefined байвал төрлийг заавал тодорхой зааж өгнө:
interface User {
id: number;
name: string;
email: string;
}
// User объект эсвэл null байна — <User | null> гэж заана
const [user, setUser] = useState<User | null>(null);
// Массив — эхний утга хоосон массив байвал заавал заана
const [courses, setCourses] = useState<string[]>([]);
const [users, setUsers] = useState<User[]>([]);
<User | null> гэдэг нь "энэ state User объект эсвэл null байж болно" гэсэн үг. Иймд user.name гэж ашиглахаасаа өмнө user !== null гэж шалгах шаардлагатайг TypeScript сануулна.
Нарийн state object
Олон талбартай state бол interface ашиглан тодорхойлох нь хамгийн цэвэр арга:
interface FormState {
username: string;
email: string;
password: string;
isLoading: boolean;
error: string | null;
}
const initialState: FormState = {
username: "",
email: "",
password: "",
isLoading: false,
error: null,
};
function RegisterForm() {
const [form, setForm] = useState<FormState>(initialState);
const handleChange = (field: keyof FormState, value: string) => {
setForm((prev) => ({ ...prev, [field]: value }));
};
return (
<form>
<input
value={form.username}
onChange={(e) => handleChange("username", e.target.value)}
placeholder="Нэвтрэх нэр"
/>
<input
type="email"
value={form.email}
onChange={(e) => handleChange("email", e.target.value)}
placeholder="И-мэйл"
/>
{form.error && <p className="error">{form.error}</p>}
</form>
);
}
keyof FormState гэдэг нь "username" | "email" | "password" | "isLoading" | "error" гэсэн утгатай — зөвхөн тухайн interface-н талбаруудыг зөвшөөрнө.
useRef төрөлжүүлэх
useRef хоёр нийтлэг хэрэглээтэй — DOM элемент барих болон дурын утга хадгалах:
import { useRef, useEffect } from "react";
function FocusInput() {
// DOM элемент барих — HTMLInputElement төрлийг заана
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// null эсэл шалгаад л ашиглана
inputRef.current?.focus();
}, []);
return <input ref={inputRef} placeholder="Автоматаар фокус болно" />;
}
DOM-гүй, зүгээр утга хадгалахад:
function Timer() {
// render хийхгүйгээр утга хадгалах — null-гүй, шууд утга
const intervalRef = useRef<number>(0);
const countRef = useRef<number>(0);
const start = () => {
intervalRef.current = window.setInterval(() => {
countRef.current += 1;
console.log("Тоолол:", countRef.current);
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
return (
<div>
<button onClick={start}>Эхлэх</button>
<button onClick={stop}>Зогсоох</button>
</div>
);
}
useReducer төрөлжүүлэх
useReducer нь нарийн state логиктой тохиолдолд useState-н оронд хэрэглэгддэг. TypeScript-тэй хамт union type ашиглан action-уудыг тодорхойлно:
interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
}
interface CartState {
items: CartItem[];
total: number;
}
// Боломжит бүх action-уудыг union-оор тодорхойлно
type CartAction =
| { type: "ADD_ITEM"; payload: CartItem }
| { type: "REMOVE_ITEM"; payload: { id: number } }
| { type: "CLEAR_CART" };
function cartReducer(state: CartState, action: CartAction): CartState {
switch (action.type) {
case "ADD_ITEM":
return {
...state,
items: [...state.items, action.payload],
total: state.total + action.payload.price,
};
case "REMOVE_ITEM":
return {
...state,
items: state.items.filter((item) => item.id !== action.payload.id),
};
case "CLEAR_CART":
return { items: [], total: 0 };
default:
return state;
}
}
function Cart() {
const [cart, dispatch] = useReducer(cartReducer, { items: [], total: 0 });
return (
<div>
<p>Нийт: {cart.total}₮</p>
<button onClick={() => dispatch({ type: "CLEAR_CART" })}>
Сагс цэвэрлэх
</button>
</div>
);
}
TypeScript action.type-ыг таньж, ADD_ITEM action-д action.payload-д CartItem байна гэдгийг мэддэг — аль action-д ямар payload байхыг заавар харуулна.
Hooks-ыг TypeScript-тэй ашиглах нь эхэндээ нэмэлт код мэт санагдах ч алдаа буурч, код уншихад хялбар болдгийг бодит проектод мэдрэх болно.
Дараагийн хичээлд:
Товч дарах, input бичих, маягт илгээх зэрэг хэрэглэгчийн үйл явдлуудыг (events) TypeScript-тэй хэрхэн зөв боловсруулахыг практикаар үзнэ.