# 📄 JSX và component đầu tiên

By Better Wise

Ở module 1 bạn đã dựng dự án Vite và tập nhìn giao diện theo khối component. Giờ là lúc viết component thật. Toàn bộ module 2 xoay quanh một ý: bạn mô tả giao diện bằng dữ liệu và cấu trúc, React lo việc cập nhật DOM. Bài này trang bị cú pháp để làm được điều đó.

Sau bài học này, bạn sẽ:

- Viết được JSX và phân biệt rõ nó khác HTML ở đâu.
- Nhúng biểu thức JavaScript vào JSX bằng cặp ngoặc nhọn.
- Tách một màn hình thành component cha và component con.
- Nhận ra 4 lỗi JSX mà người mới gần như ai cũng mắc.

## JSX trông giống HTML nhưng không phải HTML

Mở `src/App.jsx` trong dự án Vite, bạn thấy một hàm trả về thứ trông y như HTML. Đó là **JSX**, viết tắt của JavaScript XML. Trình biên dịch của Vite sẽ chuyển JSX thành lời gọi `React.createElement(...)` trước khi trình duyệt nhìn thấy nó. Nghĩ đơn giản: JSX là cách viết giao diện bằng cú pháp quen thuộc, nhưng chạy bên trong JavaScript.

```jsx
function App() {
  return <h1>Chào mừng đến với ứng dụng quản lý công việc</h1>;
}
```

Vì nằm trong JavaScript, JSX phải nhường một số từ khóa cho ngôn ngữ chủ. Bảng dưới là những chỗ bạn sẽ gặp ngay trong buổi đầu:

| HTML quen thuộc | Viết trong JSX | Vì sao |
| --- | --- | --- |
| `class="card"` | `className="card"` | `class` là từ khóa của JavaScript |
| `for="task"` | `htmlFor="task"` | `for` cũng là từ khóa |
| `<input type="text">` | `<input type="text" />` | Mọi thẻ trong JSX phải đóng |
| `style="color: red"` | `style=_____` | `style` nhận một object |
| `` | `{/* ghi chú */}` | Bình luận nằm trong biểu thức |

> **Note:** 
> JSX bắt buộc trả về **một phần tử gốc duy nhất**. Muốn trả về nhiều phần tử ngang cấp, bọc chúng trong `<div>` hoặc dùng Fragment viết tắt `<>...</>`.

**Bài tập 1.** Đoạn JSX sau có 4 lỗi. Tìm và sửa.

```jsx
function Card() {
  return (
    <div class="card">
      <label for="task">Tên việc</label>
      <input type="text">
      <img src="logo.png">
    </div>
  );
}
```

**Xem đáp án**

Bốn lỗi là `class`, `for`, và hai thẻ chưa tự đóng. Bản đúng:

```jsx
function Card() {
  return (
    <div className="card">
      <label htmlFor="task">Tên việc</label>
      <input type="text" />
      <img src="logo.png" />
    </div>
  );
}
```

## Nhúng JavaScript vào JSX bằng ngoặc nhọn

Đây là phần làm JSX mạnh hơn HTML: bất kỳ biểu thức JavaScript nào đặt trong `{}` đều được tính giá trị và chèn vào giao diện.

```jsx
function TaskSummary() {
  const ten = 'Lan';
  const tongViec = 3;
  const daXong = 1;

  return (
    <section>
      <h2>Xin chào, {ten}</h2>
      <p>Tổng cộng {tongViec} việc, đã xong {daXong}.</p>
      <p>Còn lại: {tongViec - daXong} việc</p>
      <p>Chữ hoa: {ten.toUpperCase()}</p>
    </section>
  );
}
```

Bạn nhúng được biến, phép tính, gọi hàm, truy cập thuộc tính. Nhưng chỉ **biểu thức**, không phải câu lệnh. Viết `{if (daXong > 0) ...}` là lỗi cú pháp ngay. Muốn rẽ nhánh, dùng toán tử ba ngôi hoặc `&&`:

```jsx
{conLai > 0 ? <p>Còn {conLai} việc</p> : <p>Đã xong hết!</p>}
{laAdmin && <span className="badge">Quản trị</span>}
```

**Bài tập 2.** Cho `const tasks = ['Học JSX', 'Làm bài tập', 'Xây app Vite'];` và `const done = 1;`. Viết JSX hiển thị tổng số việc, và chỉ hiện dòng "Còn N việc" khi N lớn hơn 0.

**Gợi ý và đáp án**

Tính `const conLai = tasks.length - done;` trước, rồi dùng `&&` để điều kiện hóa:

```jsx
function Summary() {
  const conLai = tasks.length - done;
  return (
    <div>
      <p>Tổng cộng: {tasks.length} việc</p>
      {conLai > 0 && <p>Còn {conLai} việc</p>}
    </div>
  );
}
```

Nhớ: nếu `conLai` là 0, biểu thức `0 && ...` trả về `0` và React sẽ in ra số 0. Với trường hợp đó hãy dùng toán tử ba ngôi.

## Component đầu tiên: một hàm trả về JSX

Component trong React chỉ là một hàm JavaScript. Nhận vào dữ liệu (bài sau sẽ nói về props), trả về JSX. Hai quy tắc bắt buộc:

1. Tên component viết **PascalCase**, chữ cái đầu viết hoa: `TaskSummary`, `AppHeader`.
2. Hàm phải trả về JSX hoặc `null`.

Quy tắc chữ hoa không phải chuyện thẩm mỹ. React dựa vào chữ cái đầu để phân biệt: `<header>` là thẻ HTML, `<Header />` là component của bạn. Viết `<header />` khi định gọi component `Header` là lỗi rất hay gặp vì không báo lỗi, chỉ hiện sai.

Khi dùng component, bạn gọi nó như thẻ tự đóng: `<AppHeader />`, tương đương `<AppHeader></AppHeader>`.

## Tách giao diện thành component cha con

Quy tắc thực dụng: một hàm dài quá khoảng 30 dòng, hoặc một khối giao diện có ranh giới rõ ràng (header, danh sách, footer), thì tách ra. Component `App` đóng vai cha, các component nhỏ là con.

```mermaid
flowchart LR
    App["🧩<br/>App"] --> Header["🏷️<br/>AppHeader"]
    App --> Summary["📊<br/>TaskSummary"]
    App --> Footer["📌<br/>AppFooter"]
```

```jsx
function AppHeader() {
  return (
    <header>
      <h1>Quản lý công việc</h1>
    </header>
  );
}

function AppFooter() {
  return <footer>Học React cùng Vite</footer>;
}

function App() {
  return (
    <div>
      <AppHeader />
      <TaskSummary />
      <AppFooter />
    </div>
  );
}

export default App;
```

Vài dự án thật thường đặt mỗi component một file cho dễ đọc:

```tree
src/
  components/
    AppHeader.jsx
    AppFooter.jsx
    TaskSummary.jsx
  App.jsx
  main.jsx
```

**Bài tập 3.** Lấy đoạn JSX của bài tập 2 và tách thành `TaskSummary` (hiển thị số liệu) cùng `App` (bọc ngoài). Sau đó thêm `AppHeader` phía trên.

**Hướng làm**

Tạo ba hàm riêng, mỗi hàm trả về một khối JSX duy nhất, rồi `App` gọi cả ba bên trong một `<div>`. Kiểm tra bằng mắt: nếu trong `App` còn phép tính `tasks.length - done` thì bạn chưa tách xong, phần tính toán nên nằm trong `TaskSummary`.

## Lỗi thường gặp khi viết JSX lần đầu

- **Quên chữ hoa**: `<appHeader />` bị coi là thẻ HTML lạ và không render gì. Luôn dùng `<AppHeader />`.
- **Trả về hai phần tử gốc**: bọc trong `<>...</>` thay vì thêm `div` vô nghĩa.
- **Dùng `class`, `for`**: đổi ngay sang `className`, `htmlFor`. React sẽ cảnh báo trong console.
- **Nhúng câu lệnh vào `{}`**: chỉ biểu thức mới hợp lệ. `if`, `for`, `const` phải nằm ngoài phần `return`.
- **Quên `key` khi render mảng**: chưa cần ở bài này, nhưng nhớ rằng bài 2.4 sẽ quay lại.

Mẹo nhỏ: giữ mỗi component trả về một khối giao diện có ý nghĩa, đừng tách vụn thành từng thẻ. Tách theo ranh giới giao diện, không tách theo số dòng.

## Chốt lại và bước tiếp theo

Ba điều cần mang theo: JSX là HTML viết trong JavaScript với vài khác biệt cú pháp (`className`, `htmlFor`, thẻ tự đóng, một phần tử gốc); ngoặc nhọn `{}` là cửa để đưa biểu thức JavaScript vào giao diện; và component là hàm PascalCase trả về JSX, được gọi như thẻ tự đóng.

Hiện tại các component con của bạn chưa nhận được dữ liệu nào từ cha. Bài tiếp theo, **Props và composition**, sẽ giải quyết đúng điểm đó: truyền dữ liệu xuống component con để một `TaskSummary` dùng lại được cho nhiều bộ số liệu khác nhau.

JSX dùng thuộc tính _____ thay cho `class` của HTML.
Nhúng biểu thức JavaScript vào JSX bằng cặp {{c2::ngoặc nhọn {}}}.
Tên component React phải bắt đầu bằng _____.
JSX chỉ trả về _____, hoặc dùng Fragment `<>...</>`.
Trong JSX, thẻ không có nội dung như `input` phải _____.
Vì không viết được câu lệnh `if` trong JSX, ta dùng _____.

Front: JSX là gì?
Back: Cú pháp giống HTML viết trong JavaScript, được biên dịch thành lời gọi React.createElement.

Front: Trong JSX, thay cho thuộc tính class của HTML ta viết gì?
Back: className, vì class là từ khóa của JavaScript.

Front: Trong JSX, thay cho thuộc tính for của HTML ta viết gì?
Back: htmlFor.

Front: Cách nhúng một biểu thức JavaScript vào JSX?
Back: Đặt biểu thức trong cặp ngoặc nhọn {}, ví dụ {ten.toUpperCase()}.

Front: Component React là gì?
Back: Một hàm JavaScript nhận dữ liệu và trả về JSX (hoặc null).

Front: Tên component phải viết theo quy ước nào?
Back: PascalCase, chữ cái đầu viết hoa, ví dụ AppHeader.

Front: Vì sao JSX phải có một phần tử gốc duy nhất?
Back: Vì hàm chỉ trả về được một giá trị; nhiều phần tử ngang cấp cần bọc trong div hoặc Fragment.

Front: Fragment viết tắt trong JSX trông thế nào?
Back: <> ... </>, dùng khi cần bọc nhiều phần tử mà không thêm div.

Front: Viết bình luận trong JSX?
Back: {/* nội dung bình luận */}.

Front: Inline style trong JSX viết thế nào?
Back: Bằng object có thuộc tính camelCase: style={{ color: 'red' }}.

Front: Không dùng được câu lệnh if trong JSX, vậy render có điều kiện bằng gì?
Back: Toán tử ba ngôi (a ? b : c) hoặc toán tử &&.

Front: Điều gì xảy ra khi viết <header /> để gọi component Header?
Back: React hiểu đó là thẻ HTML header, không phải component của bạn, nên component không được render.

Front: Thẻ input trong JSX cần viết thế nào?
Back: Phải tự đóng: <input type="text" />.

---

## Contents

Part of [⚛️ React từ con số 0 cho người chỉ biết HTML](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html.md)

- **🧱 Nền tảng JavaScript và tư duy React**
  - [✨ ES6 thiết yếu cho React: let, const, arrow function, destructuring, spread](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/es6-thiet-yeu-cho-react.md)
  - [🔁 Hàm và mảng trong thực tế: map, filter, reduce](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/ham-mang-map-filter-reduce.md) (sign-in required)
  - [🧩 Tư duy component và khởi tạo dự án với Vite](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/tu-duy-component-va-khoi-tao-vite.md)
  - [✅ Checkpoint: nền tảng JavaScript và tư duy React](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/checkpoint-nen-tang-javascript-va-react.md)
- **🧩 Component, JSX, props và state**
  - [📄 JSX và component đầu tiên](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/jsx-va-component-dau-tien.md) — this lesson
  - [📦 Props và composition: truyền dữ liệu giữa component](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/props-va-composition.md)
  - [🔄 State với useState và xử lý sự kiện](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/state-usestate-va-su-kien.md)
  - [📋 Render danh sách và key trong React](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/render-danh-sach-va-key.md)
  - [✅ Checkpoint: component, JSX, props và state](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/checkpoint-component-va-state.md)
- **🚀 Form, useEffect, gọi API và dự án nhỏ**
  - [📝 Form và controlled components](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/form-va-controlled-components.md)
  - [⚙️ useEffect và side effects](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/useeffect-va-side-effects.md)
  - [🌐 Gọi API với fetch: loading, error và cleanup](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/goi-api-voi-fetch.md) (premium content)
  - [🛠️ Dự án: ứng dụng quản lý nhỏ với Vite](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/du-an-ung-dung-quan-ly-voi-vite.md) (premium content)
  - [✅ Checkpoint: form, useEffect, gọi API và dự án](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/checkpoint-form-api-va-du-an.md) (premium content)
- [Course cheatsheet](https://everwise.study/courses/react-tu-con-so-0-cho-nguoi-chi-biet-html/cheatsheet.md) (premium content)
