📘 PHẦN 2: TRẠNG THÁI (STATE) VÀ TÍNH TƯƠNG TÁC
🎯 Mục tiêu tổng quát
- Hiểu khái niệm
statevà vai trò của nó trong việc tạo ra các component động. - Sử dụng Hook
useStateđể quản lý trạng thái của component. - Xử lý các sự kiện người dùng như click, nhập liệu.
- Sử dụng render có điều kiện để hiển thị hoặc ẩn các phần tử UI.
- Nắm vững cách render danh sách dữ liệu và tầm quan trọng của
key.
🧑🏫 Bài 1: State và Hook useState
State là gì?
- State là dữ liệu riêng tư của một component, có thể thay đổi theo thời gian (thường là do tương tác của người dùng).
- Khi state thay đổi, React sẽ tự động render lại (re-render) component đó để cập nhật giao diện.
- Props là để truyền dữ liệu từ cha xuống con, còn State là để quản lý dữ liệu nội tại của component.
Sơ đồ hoạt động của State:
[ Tương tác người dùng (ví dụ: click) ]
|
V
[ Gọi hàm setState() ]
|
V
[ State thay đổi ]
|
V
[ React render lại Component ]
|
V
[ Giao diện được cập nhật ]Giới thiệu Hook useState
useState là một Hook cho phép bạn thêm state vào function component.
import { useState } from 'react';
function Counter() {
// Khai báo một biến state tên là `count`
// `setCount` là hàm để cập nhật giá trị cho `count`
const [count, setCount] = useState(0); // 0 là giá trị khởi tạo
return (
<div>
<p>Bạn đã click {count} lần</p>
{/* Sẽ học ở bài sau */}
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}Cập nhật State
Không bao giờ thay đổi state trực tiếp:
count = count + 1;(SAI)Luôn luôn sử dụng hàm setter do
useStatecung cấp:setCount(count + 1);(ĐÚNG)Khi cập nhật state dựa trên giá trị cũ, nên dùng dạng callback để đảm bảo tính chính xác:
jsxsetCount(prevCount => prevCount + 1);
🧑🏫 Bài 2: Xử lý sự kiện (Handling Events)
Sự kiện trong React
- Tên sự kiện trong React được viết theo kiểu
camelCase, ví dụ:onClick,onChange. - Bạn truyền một hàm vào trình xử lý sự kiện, thay vì một chuỗi.
function AlertButton() {
function handleClick() {
alert('Bạn đã click vào nút!');
}
return (
<button onClick={handleClick}>
Bấm vào đây
</button>
);
}Truyền hàm xử lý sự kiện qua Props
Bạn có thể truyền các hàm xử lý sự kiện từ component cha xuống component con.
Sơ đồ luồng sự kiện:
[ Parent Component (định nghĩa hàm onAction) ] --(props: onAction)--> [ Child Component (Button) ]
^ |
|----------------(Khi click, gọi props.onAction())---------------|// Child: Button.jsx
function Button({ onCustomClick, children }) {
return (
<button onClick={onCustomClick}>
{children}
</button>
);
}
// Parent: App.jsx
import Button from './Button';
function App() {
function handlePlayClick() {
alert('Đang phát video!');
}
function handleUploadClick() {
alert('Đang tải lên!');
}
return (
<div>
<Button onCustomClick={handlePlayClick}>Phát video</Button>
<Button onCustomClick={handleUploadClick}>Tải lên</Button>
</div>
);
}🧑🏫 Bài 3: Render có điều kiện và List
Render có điều kiện (Conditional Rendering)
Bạn có thể dùng các biểu thức điều kiện của JavaScript để quyết định phần UI nào sẽ được render.
function UserGreeting({ isLoggedIn }) {
// Cách 1: Dùng if/else
if (isLoggedIn) {
return <h1>Chào mừng trở lại!</h1>;
}
return <h1>Vui lòng đăng nhập.</h1>;
// Cách 2: Dùng toán tử ba ngôi (thường dùng trong JSX)
return (
<div>
{isLoggedIn ? <p>Bạn đã đăng nhập</p> : <p>Bạn chưa đăng nhập</p>}
</div>
);
// Cách 3: Dùng toán tử && (chỉ render nếu điều kiện đúng)
return (
<div>
{isLoggedIn && <p>Giỏ hàng của bạn</p>}
</div>
);
}Render danh sách và thuộc tính key
Sử dụng hàm .map() để biến một mảng dữ liệu thành một mảng các phần tử React.
keylà một thuộc tính đặc biệt bạn cần cung cấp khi tạo danh sách các phần tử.keygiúp React xác định phần tử nào đã thay đổi, được thêm vào hoặc bị xóa đi.keyphải là một chuỗi hoặc số duy nhất trong danh sách anh em của nó. Thường dùngidcủa dữ liệu.
function ProductList({ products }) {
const listItems = products.map(product =>
<li key={product.id}>
{product.name}
</li>
);
return <ul>{listItems}</ul>;
}Lưu ý: Không nên dùng index của mảng làm key nếu danh sách có thể thay đổi thứ tự, thêm, xóa.
🧑🏫 Bài 4: Xử lý Form cơ bản
Controlled Components
Trong HTML, các thẻ form như <input>, <textarea> tự quản lý state của chúng. Trong React, chúng ta có thể làm cho component React trở thành "nguồn chân lý duy nhất" bằng cách sử dụng state. Một thẻ input có giá trị được kiểm soát bởi React được gọi là "controlled component".
import { useState } from 'react';
function NameForm() {
const [name, setName] = useState('');
function handleChange(event) {
setName(event.target.value);
}
function handleSubmit(event) {
event.preventDefault(); // Ngăn trình duyệt reload
alert('Tên đã được gửi: ' .concat(name));
}
return (
<form onSubmit={handleSubmit}>
<label>
Tên:
<input type="text" value={name} onChange={handleChange} />
</label>
<input type="submit" value="Gửi" />
</form>
);
}🧪 BÀI TẬP LỚN CUỐI PHẦN: Thêm giỏ hàng và tương tác cho "SimpleStore"
Mô tả bài toán
Nâng cấp trang "SimpleStore" đã tạo ở Phần 1. Thêm chức năng cho phép người dùng "Thêm vào giỏ hàng" và xem tổng số lượng sản phẩm trong giỏ.
Yêu cầu
- Component
Header.jsx:- Sử dụng
useStateđể quản lý số lượng sản phẩm trong giỏ hàng (cartCount). - Hiển thị
cartCountở góc trên bên phải của header. Ví dụ:Giỏ hàng (0).
- Sử dụng
- Component
App.jsx:- Quản lý state của giỏ hàng, có thể là một mảng các sản phẩm trong giỏ:
const [cart, setCart] = useState([]); - Viết một hàm
handleAddToCart(product)để xử lý việc thêm sản phẩm vào giỏ hàng. Hàm này sẽ được truyền xuốngProductListvàProductCard.- Logic bên trong:
setCart(prevCart => [...prevCart, product]);
- Logic bên trong:
- Quản lý state của giỏ hàng, có thể là một mảng các sản phẩm trong giỏ:
- Component
ProductCard.jsx:- Nhận hàm
onAddToCarttừprops. - Thêm một nút "Thêm vào giỏ hàng".
- Khi click vào nút này, gọi hàm
onAddToCartvà truyền thông tin sản phẩm của card đó vào.
- Nhận hàm
- Kết nối các component:
Appcomponent sẽ truyền hàmhandleAddToCartxuốngProductList.ProductListsẽ truyền tiếp hàm đó xuống từngProductCard.- Khi một sản phẩm được thêm vào giỏ hàng (state
carttrongAppthay đổi),Appsẽ tính toán tổng số lượng và truyền xuốngHeaderđể cập nhật hiển thị.- Sơ đồ:
App (state: cart) --(props: cart.length)--> Header - Sơ đồ:
App (hàm: handleAddToCart) --props--> ProductList --props--> ProductCard
- Sơ đồ:
Mục tiêu: Kết thúc phần này, ứng dụng của bạn sẽ có tính tương tác. Người dùng có thể click vào nút và thấy giao diện (số lượng trong giỏ hàng) được cập nhật ngay lập tức.
