Một Ví Dụ Thực Hành: Tính Năng "Yêu Thích"
Hãy cùng xây một tính năng nhỏ từ đầu đến cuối: người dùng có thể đánh dấu các mục trong danh sách là yêu thích, và xem số lượng họ đã yêu thích.
Cắt lát tính năng
Đây là cách tôi sẽ chia nó thành các bước dọc:
- Thêm một nút hình ngôi sao bên cạnh mỗi mục (chưa làm gì cả, chỉ hiện ra).
- Bấm vào ngôi sao sẽ chuyển đổi trạng thái bật/tắt (chỉ là state, chưa lưu).
- Hiển thị bộ đếm "★ 3 favorites" phản ánh các lần chuyển đổi.
- Lưu mục yêu thích để chúng còn tồn tại sau khi tải lại trang.
Chú ý mỗi bước đều có thể chạy và nhìn thấy được một cách độc lập. Bạn có thể dừng lại sau bất kỳ bước nào trong số này và vẫn có một ứng dụng hoạt động.
Bước 1: nút bấm
Một tốt nêu rõ tên file, thay đổi cần làm, và cái gì không được đụng vào:
In the ItemCard component (src/components/ItemCard.jsx), add a star
button in the top-right corner of each card. It should render an
outline star icon. Don't wire up any click behavior yet — just make
it appear. Keep the rest of the card layout unchanged.
Chạy ứng dụng. Bạn có thấy các ngôi sao không? Tốt. :
git add -A && git commit -m "Add star button to item cards"
Bước 2: chuyển đổi trạng thái (state)
Make the star button toggle between filled and outline when clicked.
Track this with local component state — don't save it anywhere yet.
A filled star means favorited.
Hãy nghĩ về state như trí nhớ ngắn hạn của ứng dụng — thứ đang đúng ngay lúc này, ví dụ như một ngôi sao cụ thể hiện đang được tô đầy hay không. Giờ bạn đang xử lý state và UI cùng nhau, và điều đó là bình thường. (UI, tức giao diện người dùng, chỉ đơn giản là phần ứng dụng mà một người nhìn thấy và bấm vào.) AI có lẽ sẽ thêm gì đó như:
const [isFavorite, setIsFavorite] = useState(false);
<button onClick={() => setIsFavorite(!isFavorite)}>
{isFavorite ? <StarFilled /> : <StarOutline />}
</button>
Bấm thử vài ngôi sao. Chúng có chuyển đổi không? Commit.
Bước 3: bộ đếm
Add a "★ N favorites" counter at the top of the item list. It should
count how many items currently have their star filled. Lift the
favorite state up to the list component if you need to so the counter
can see it.
Bước này có thể yêu cầu AI tái cấu trúc nơi state đang sống — đó là điều dự kiến, và chính vì vậy bạn mới làm nó thành một bước nhỏ riêng. Chạy nó, chuyển đổi vài ngôi sao, xem con số thay đổi. Commit.
Bước 4: làm cho nó lưu lại (persist)
Persist favorites so they survive a page reload. Use localStorage for
now — save the list of favorited item IDs whenever it changes, and
load it on startup.
(localStorage chỉ đơn giản là một cuốn sổ tay nhỏ mà trình duyệt giữ trên chính máy tính của người truy cập, để ứng dụng có thể ghi chú lại và tìm thấy chúng sau khi tải lại trang.) Tải lại trang. Vẫn còn được đánh dấu yêu thích chứ? Bạn vừa mới ra mắt một tính năng, từng lát cắt chạy được một lần.
Đây là cách các mảnh của tính năng hoàn chỉnh giao tiếp với nhau. Một cú bấm chảy xuống state, màn hình vẽ lại từ state, và state được phản chiếu sang bộ lưu trữ để nó tồn tại sau khi tải lại:
┌─────────────────────────────────────────────┐
│ │
click star redraws from
│ (event) state
▼ ▲
┌────────┐ updates ┌─────────┐ feeds ┌────────┐
│ UI │─────────────▶│ STATE │───────────▶│ UI │
│(button)│ │(in app │ │(filled │
└────────┘ │ memory) │ │ star + │
└────┬────┘ │counter)│
│ save on change └────────┘
▼
┌──────────┐
│ STORAGE │ ◀── load on startup,
│(localSt.)│ so it survives reload
└──────────┘