React Hooks
React Hooks
Phần tiêu đề “React Hooks”Hooks cho phép sử dụng state và các tính năng React trong function components.
useState
Phần tiêu đề “useState”Quản lý local state:
import { useState } from "react";
function Counter() { const [count, setCount] = useState(0);
return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>+</button> <button onClick={() => setCount(count - 1)}>-</button> <button onClick={() => setCount(0)}>Reset</button> </div> );}Multiple State Variables
Phần tiêu đề “Multiple State Variables”function Form() { const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [age, setAge] = useState(0);
// Hoặc dùng object const [user, setUser] = useState({ name: "", email: "", age: 0, });
const updateUser = (field, value) => { setUser((prev) => ({ ...prev, [field]: value })); };}useEffect
Phần tiêu đề “useEffect”Side effects (API calls, subscriptions, timers):
import { useEffect, useState } from "react";
function UserProfile({ userId }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true);
useEffect(() => { // Chạy sau mỗi render console.log("Component rendered"); });
useEffect(() => { // Chỉ chạy lần đầu (mount) console.log("Component mounted"); }, []);
useEffect(() => { // Chạy khi userId thay đổi setLoading(true); fetch(`/api/users/${userId}`) .then((r) => r.json()) .then((data) => { setUser(data); setLoading(false); }); }, [userId]);
useEffect(() => { // Cleanup function const timer = setInterval(() => { console.log("Tick"); }, 1000);
return () => { clearInterval(timer); // Cleanup khi unmount }; }, []);
if (loading) return <div>Loading...</div>; return <div>{user?.name}</div>;}useContext
Phần tiêu đề “useContext”Chia sẻ data qua component tree:
import { createContext, useContext, useState } from "react";
// Tạo contextconst ThemeContext = createContext();
// Providerfunction App() { const [theme, setTheme] = useState("light");
return ( <ThemeContext.Provider value={{ theme, setTheme }}> <Toolbar /> </ThemeContext.Provider> );}
// Consumerfunction Toolbar() { const { theme, setTheme } = useContext(ThemeContext);
return ( <div className={theme}> <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>Toggle Theme</button> </div> );}useRef
Phần tiêu đề “useRef”Giữ reference đến DOM hoặc mutable value:
import { useRef, useEffect } from "react";
function TextInput() { const inputRef = useRef(null);
useEffect(() => { // Focus input khi mount inputRef.current.focus(); }, []);
return <input ref={inputRef} type="text" />;}
// Lưu giá trị không trigger re-renderfunction Timer() { const countRef = useRef(0);
useEffect(() => { const timer = setInterval(() => { countRef.current += 1; console.log(countRef.current); }, 1000);
return () => clearInterval(timer); }, []);}useMemo
Phần tiêu đề “useMemo”Memoize expensive calculations:
import { useMemo, useState } from "react";
function ExpensiveComponent({ items }) { const [filter, setFilter] = useState("");
// Chỉ tính lại khi items hoặc filter thay đổi const filteredItems = useMemo(() => { console.log("Filtering..."); return items.filter((item) => item.name.toLowerCase().includes(filter.toLowerCase())); }, [items, filter]);
return ( <div> <input value={filter} onChange={(e) => setFilter(e.target.value)} /> {filteredItems.map((item) => ( <div key={item.id}>{item.name}</div> ))} </div> );}useCallback
Phần tiêu đề “useCallback”Memoize functions:
import { useCallback, useState } from "react";
function Parent() { const [count, setCount] = useState(0);
// Function reference không đổi const handleClick = useCallback(() => { setCount((c) => c + 1); }, []);
return <Child onClick={handleClick} />;}
// Child chỉ re-render khi onClick thay đổiconst Child = React.memo(({ onClick }) => { console.log("Child rendered"); return <button onClick={onClick}>Click</button>;});useReducer
Phần tiêu đề “useReducer”State phức tạp với reducer pattern:
import { useReducer } from "react";
const initialState = { count: 0 };
function reducer(state, action) { switch (action.type) { case "increment": return { count: state.count + 1 }; case "decrement": return { count: state.count - 1 }; case "reset": return initialState; default: throw new Error(); }}
function Counter() { const [state, dispatch] = useReducer(reducer, initialState);
return ( <> Count: {state.count} <button onClick={() => dispatch({ type: "increment" })}>+</button> <button onClick={() => dispatch({ type: "decrement" })}>-</button> <button onClick={() => dispatch({ type: "reset" })}>Reset</button> </> );}Custom Hooks
Phần tiêu đề “Custom Hooks”Tái sử dụng logic:
// useFetch custom hookfunction useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null);
useEffect(() => { setLoading(true); fetch(url) .then((r) => r.json()) .then((data) => { setData(data); setLoading(false); }) .catch((err) => { setError(err); setLoading(false); }); }, [url]);
return { data, loading, error };}
// Sử dụngfunction UserList() { const { data, loading, error } = useFetch("/api/users");
if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>;
return ( <ul> {data.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> );}More Custom Hooks Examples
Phần tiêu đề “More Custom Hooks Examples”// useLocalStoragefunction useLocalStorage(key, initialValue) { const [value, setValue] = useState(() => { const stored = localStorage.getItem(key); return stored ? JSON.parse(stored) : initialValue; });
useEffect(() => { localStorage.setItem(key, JSON.stringify(value)); }, [key, value]);
return [value, setValue];}
// useDebouncefunction useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => { const timer = setTimeout(() => { setDebouncedValue(value); }, delay);
return () => clearTimeout(timer); }, [value, delay]);
return debouncedValue;}
// useWindowSizefunction useWindowSize() { const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight, });
useEffect(() => { const handleResize = () => { setSize({ width: window.innerWidth, height: window.innerHeight, }); };
window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []);
return size;}Rules of Hooks
Phần tiêu đề “Rules of Hooks”- ✅ Chỉ gọi ở top level - Không trong loops, conditions, nested functions
- ✅ Chỉ gọi trong React functions - Components hoặc custom hooks
- ✅ Custom hooks bắt đầu với “use” - Convention naming
Best Practices
Phần tiêu đề “Best Practices”- Ưu tiên useState cho simple state
- Dùng useReducer cho complex state logic
- useMemo/useCallback chỉ khi cần optimize
- Tạo custom hooks để reuse logic
- Cleanup trong useEffect để tránh memory leaks
Bước tiếp theo
Phần tiêu đề “Bước tiếp theo”- 👉 Performance
- 📚 Testing