React Hooks - useState và useEffect

React Hooks cho phép sử dụng state và lifecycle features trong function components. Hãy cùng tìm hiểu 2 hooks phổ biến nhất!

1. useState - Quản lý State

Hook để thêm state vào function component:

import React, { useState } from 'react';

function Counter() {
    // Khai báo state variable
    const [count, setCount] = useState(0);
    
    return (
        <div>
            <p>Bạn đã click {count} lần</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
        </div>
    );
}

// Multiple state variables
function UserForm() {
    const [name, setName] = useState('');
    const [email, setEmail] = useState('');
    const [age, setAge] = useState(0);
    
    return (
        <form>
            <input 
                value={name}
                onChange={(e) => setName(e.target.value)}
            />
            <input 
                value={email}
                onChange={(e) => setEmail(e.target.value)}
            />
        </form>
    );
}

2. useState với Objects và Arrays

Cần spread operator để update state object/array:

function UserProfile() {
    const [user, setUser] = useState({
        name: 'Huy',
        age: 22,
        city: 'Hanoi'
    });
    
    // Update object - phải spread
    const updateName = (newName) => {
        setUser({
            ...user,
            name: newName
        });
    };
    
    // Hoặc dùng functional update
    const incrementAge = () => {
        setUser(prevUser => ({
            ...prevUser,
            age: prevUser.age + 1
        }));
    };
}

function TodoList() {
    const [todos, setTodos] = useState([]);
    
    // Thêm item
    const addTodo = (text) => {
        setTodos([...todos, { id: Date.now(), text }]);
    };
    
    // Xóa item
    const removeTodo = (id) => {
        setTodos(todos.filter(todo => todo.id !== id));
    };
}

3. useEffect - Side Effects

Hook để xử lý side effects (API calls, subscriptions, DOM manipulation):

import React, { useState, useEffect } from 'react';

function UserData() {
    const [user, setUser] = useState(null);
    
    // Chạy sau mỗi render
    useEffect(() => {
        console.log('Component rendered');
    });
    
    // Chạy chỉ 1 lần khi mount ([] dependency)
    useEffect(() => {
        fetch('https://api.example.com/user')
            .then(res => res.json())
            .then(data => setUser(data));
    }, []); // Empty array = chỉ chạy lần đầu
    
    // Chạy khi user thay đổi
    useEffect(() => {
        console.log('User changed:', user);
    }, [user]); // Dependency array
    
    return <div>{user?.name}</div>;
}

4. Cleanup Function

Return function trong useEffect để cleanup:

function Timer() {
    const [seconds, setSeconds] = useState(0);
    
    useEffect(() => {
        // Setup
        const interval = setInterval(() => {
            setSeconds(s => s + 1);
        }, 1000);
        
        // Cleanup function
        return () => {
            clearInterval(interval);
        };
    }, []); // Chỉ setup/cleanup khi mount/unmount
    
    return <div>{seconds} seconds</div>;
}

function WindowSize() {
    const [width, setWidth] = useState(window.innerWidth);
    
    useEffect(() => {
        const handleResize = () => setWidth(window.innerWidth);
        
        window.addEventListener('resize', handleResize);
        
        // Cleanup: remove listener khi unmount
        return () => {
            window.removeEventListener('resize', handleResize);
        };
    }, []);
}

5. Fetch Data Pattern

Pattern phổ biến để fetch data:

function DataFetcher() {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
    
    useEffect(() => {
        const fetchData = async () => {
            try {
                setLoading(true);
                const response = await fetch('https://api.example.com/data');
                
                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }
                
                const result = await response.json();
                setData(result);
                setError(null);
            } catch (err) {
                setError(err.message);
                setData(null);
            } finally {
                setLoading(false);
            }
        };
        
        fetchData();
    }, []);
    
    if (loading) return <div>Loading...</div>;
    if (error) return <div>Error: {error}</div>;
    if (!data) return null;
    
    return <div>{/* Render data */}</div>;
}

6. Common Mistakes

// ❌ SAI - Infinite loop
useEffect(() => {
    setCount(count + 1);
}); // Không có dependency array

// ❌ SAI - Async function trực tiếp
useEffect(async () => {
    const data = await fetchData();
}, []);

// ✅ ĐÚNG - Async bên trong
useEffect(() => {
    const loadData = async () => {
        const data = await fetchData();
        setData(data);
    };
    loadData();
}, []);

Kết luận

useState và useEffect là 2 hooks cơ bản nhất trong React:

Thành thạo 2 hooks này là nền tảng để học các hooks khác!