JSX là một phần mở rộng cú pháp của JavaScript, cho phép bạn viết markup giống HTML ngay trong code. Component React trả về JSX để mô tả những gì sẽ hiện trên màn hình, và một công cụ build sẽ biên dịch nó thành các lời gọi hàm JavaScript thông thường. Nó trông giống HTML, nhưng có vài quy tắc khác, và dấu ngoặc nhọn cho phép bạn đưa bất kỳ giá trị JavaScript nào vào markup.
Mọi thứ bên trong {} đều là JavaScript: một thuộc tính, một lời gọi phương thức, thậm chí cả map biến một mảng thành các mục danh sách. Thêm 'design' vào mảng skills và cả số đếm lẫn danh sách đều cập nhật.
Các quy tắc của JSX
Trả về một phần tử gốc
Component trả về một giá trị duy nhất, nên JSX của nó cần một phần tử cha duy nhất. Hai phần tử ngang hàng ở cấp trên cùng là lỗi cú pháp. Khi không muốn có thêm một <div> trên trang, hãy bọc chúng trong một fragment, <>...</>:
// Error: Adjacent JSX elements must be wrapped in an enclosing tag
return (
<h1>Title</h1>
<p>Text</p>
);
// OK: a fragment groups them without adding a DOM element
return (
<>
<h1>Title</h1>
<p>Text</p>
</>
);
Khi cần đặt key cho cả nhóm, hãy dùng dạng đầy đủ <Fragment key={id}>; xem fragment.
Đóng mọi thẻ
HTML cho phép để mở một số thẻ. JSX thì không. Phần tử không có phần tử con tự đóng bằng />: <img />, <br />, <input />, và các component của riêng bạn như <Avatar />.
Dùng className, htmlFor và thuộc tính camelCase
Thuộc tính JSX trở thành khóa của một đối tượng JavaScript, nên chúng tuân theo cách đặt tên của JavaScript. class và for là từ khóa dành riêng trong JavaScript, vì vậy JSX dùng className và htmlFor. Thuộc tính nhiều từ và mọi trình xử lý sự kiện đều viết theo camelCase: tabIndex, maxLength, autoFocus, onClick, onChange. Hai ngoại lệ giữ nguyên dấu gạch nối: thuộc tính aria-* và data-* được viết y như trong HTML.
Đổi className="field" thành class="field". Nhãn vẫn giữ style, vì React vẫn đặt thuộc tính đó, nhưng bản build phát triển sẽ ghi log "Invalid DOM property class. Did you mean className?" Phần xem trước chạy như bản build production, nên bạn sẽ không thấy cảnh báo đó ở đây; hãy dùng className. Thuộc tính dạng chuỗi dùng dấu nháy (type="email"); mọi giá trị khác dùng dấu ngoặc nhọn (maxLength={40}, disabled={false}).
Dấu ngoặc nhọn nhận biểu thức, không nhận câu lệnh
Bên trong {} bạn có thể viết bất cứ thứ gì tạo ra một giá trị: một biến, phép tính, lời gọi hàm, toán tử ba ngôi, &&, template literal. Các câu lệnh như if, for và const không tạo ra giá trị, nên không thể đặt ở đó. Hãy xử lý trước return, hoặc dùng dạng biểu thức:
Nhìn kỹ trước khi click: trang đang hiện một số 0 lạc chỗ. count && <p>...</p> cho ra 0 khi số đếm bằng không, và React render số, kể cả số không. Đổi thành count > 0 && <p>You have mail.</p> và số 0 biến mất. false, null, undefined và true không render ra gì, vì vậy đặt một boolean bên trái && là an toàn. Trang render có điều kiện trình bày các cách viết này.
Style nội tuyến dùng một đối tượng
Thuộc tính style nhận một đối tượng JavaScript, không phải chuỗi CSS. Đó là lý do có kiểu "ngoặc nhọn kép": cặp bên ngoài nghĩa là "đây là JavaScript", cặp bên trong là đối tượng literal. Tên thuộc tính viết theo camelCase (backgroundColor, fontSize), và một số thuần nghĩa là pixel với hầu hết các thuộc tính kích thước.
Vì style là một đối tượng, nó có thể được tính từ state như mọi giá trị khác. Thử đổi fontSize: big ? 24 : 16 thành fontSize: big ? '2em' : '1em': chuỗi được truyền nguyên vẹn kèm đơn vị.
Chú thích
Chú thích bên trong JSX được đặt trong dấu ngoặc nhọn: {/* this is a comment */}. Chú thích HTML (<!-- -->) là lỗi cú pháp. Bên ngoài markup, trong thân hàm, chú thích // thông thường vẫn dùng được như bình thường.
return (
<div>
{/* The header is hidden on small screens */}
<Header />
</div>
);
Chuyển HTML sang JSX
Dán HTML vào một component là bước đầu thường gặp, và phần lớn sẽ chạy được. Đây là những thay đổi bạn thường phải làm:
classthànhclassName, vàforthànhhtmlFor.- Thẻ rỗng tự đóng:
<br>thành<br />,<img src="a.png">thành<img src="a.png" />. style="color: red; font-size: 14px"thànhstyle={{ color: 'red', fontSize: 14 }}.- Thuộc tính sự kiện nhận một hàm, không phải chuỗi:
onclick="save()"thànhonClick={save}. - Tên thuộc tính chuyển sang camelCase:
tabindexthànhtabIndex, vàstroke-widthcủa SVG thànhstrokeWidth. <!-- comments -->thành{/* comments */}.- Nhiều phần tử ở cấp trên cùng cần một phần tử cha hoặc một fragment.
Các công cụ chuyển HTML sang JSX trực tuyến làm những bước này giúp bạn, nhưng nắm được chúng sẽ giúp bạn dễ đọc thông báo lỗi khi bỏ sót một bước.
JSX được biên dịch thành gì
Trình duyệt không thể chạy JSX. Một trình biên dịch (như Babel, SWC hoặc Oxc, được Vite và các framework chạy giúp bạn) biến mỗi thẻ thành một lời gọi hàm từ react/jsx-runtime:
// What you write
const element = <h1 className="title">Hello, {name}</h1>;
const button = <Button size="small">OK</Button>;
// What the compiler produces (roughly)
import { jsx as _jsx, jsxs as _jsxs } from 'react/jsx-runtime';
const element = _jsxs('h1', { className: 'title', children: ['Hello, ', name] });
const button = _jsx(Button, { size: 'small', children: 'OK' });
Thẻ viết thường trở thành một chuỗi ('h1', một phần tử HTML), thẻ viết hoa chữ đầu trở thành tham chiếu đến hàm của bạn (Button), các thuộc tính trở thành đối tượng props, và nội dung trở thành children. Các thiết lập cũ biên dịch thành React.createElement(type, props, ...children), đó là lý do các tệp cũ bắt đầu bằng import React from 'react' dù không hề nhắc đến React. Lời gọi này trả về một đối tượng thuần gọi là phần tử React (React element). Bạn có thể ghi log nó:
Console cho thấy heading chỉ là dữ liệu: một kiểu 'h3' và một đối tượng props. Đó cũng là lý do JSX có thể được lưu trong biến, truyền vào hàm và trả về từ hàm.
JSX tự escape văn bản cho bạn
Mọi chuỗi bạn đặt trong dấu ngoặc nhọn đều được render thành văn bản, không bao giờ thành HTML. Nếu người dùng gõ <img src=x onerror=alert(1)> vào ô bình luận, React hiển thị đúng các ký tự đó trên màn hình thay vì tạo ra một thẻ ảnh. Nhờ vậy dạng tấn công cross-site scripting (XSS) phổ biến nhất bị chặn ngay từ mặc định.
Đoạn văn đầu tiên in các thẻ ra dưới dạng ký tự thường. Đoạn thứ hai dùng dangerouslySetInnerHTML, thuộc tính này bỏ qua việc escape và chèn chuỗi vào như HTML thật, nên "our own" được in đậm. Cái tên dài chính là lời cảnh báo: chỉ truyền vào HTML do chính bạn tạo ra hoặc đã được làm sạch bằng một công cụ như DOMPurify. Truyền dữ liệu người dùng nhập vào đó sẽ mở lại lỗ hổng XSS mà JSX đã đóng.
Câu hỏi thường gặp
JSX là viết tắt của gì?
JSX là viết tắt của JavaScript XML. Đây là một phần mở rộng cú pháp cho phép bạn viết markup trông giống HTML bên trong code JavaScript.
JSX có phải là HTML không?
Không. JSX trông giống HTML nhưng được biên dịch thành các lời gọi hàm JavaScript tạo ra phần tử React. Vì vậy nó dùng className thay cho class, tên thuộc tính dạng camelCase, và yêu cầu mọi thẻ đều phải được đóng.
Có thể dùng React mà không cần JSX không?
Có. JSX là tùy chọn: bạn có thể gọi trực tiếp createElement('h1', { className: 'title' }, 'Hello') từ react. Gần như mọi dự án React đều dùng JSX vì markup lồng nhau dễ đọc hơn nhiều.
Vì sao không dùng được câu lệnh if bên trong JSX?
Dấu ngoặc nhọn trong JSX nhận biểu thức, tức là thứ tạo ra một giá trị, còn if là một câu lệnh. Hãy dùng toán tử ba ngôi (cond ? a : b), &&, hoặc tính giá trị vào một biến trước return.
Làm sao viết chú thích trong JSX?
Bọc chú thích JavaScript trong dấu ngoặc nhọn: {/* like this */}. Chú thích HTML (<!-- -->) là lỗi cú pháp trong JSX.