Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

JSX & Rendering

JSX là gì?

JSX (JavaScript XML) là cú pháp mở rộng cho JavaScript, cho phép viết HTML-like code trực tiếp trong JavaScript. JSX được Babel biên dịch thành React.createElement() calls.

// JSX syntax
const element = <h1 className="title">Hello, World!</h1>;

// Biên dịch thành
const element = React.createElement(
  'h1',
  { className: 'title' },
  'Hello, World!'
);

Quy tắc JSX

// 1. Phải có một root element (hoặc Fragment)
function App() {
  return (
    <>
      <h1>Title</h1>
      <p>Content</p>
    </>
  );
}

// 2. Mọi tag phải được đóng
const img = <img src="photo.jpg" alt="photo" />;
const input = <input type="text" />;

// 3. className thay vì class
const div = <div className="container">Content</div>;

// 4. JavaScript expressions dùng {}
const name = "Alice";
const greeting = <h1>Hello, {name}!</h1>;
const result = <p>2 + 2 = {2 + 2}</p>;

// 5. Style dùng object
const styled = (
  <div style={{ color: 'red', fontSize: '16px' }}>
    Styled text
  </div>
);

Virtual DOM

React sử dụng Virtual DOM để tối ưu hóa việc cập nhật UI thực.

┌─────────────────────────────────────────────────────────┐
│                    STATE CHANGE                         │
│                        │                               │
│                        ▼                               │
│  ┌──────────────────────────────────────────────────┐  │
│  │           New Virtual DOM Tree                   │  │
│  └──────────────────────────────────────────────────┘  │
│                        │                               │
│                   DIFFING (Reconciliation)             │
│                        │                               │
│  ┌──────────────────────────────────────────────────┐  │
│  │           Old Virtual DOM Tree                   │  │
│  └──────────────────────────────────────────────────┘  │
│                        │                               │
│              Chỉ cập nhật phần thay đổi               │
│                        │                               │
│  ┌──────────────────────────────────────────────────┐  │
│  │                Real DOM                          │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘

Lợi ích:

  • Giảm thiểu DOM manipulation (tốn kém)
  • Batch updates - nhóm nhiều thay đổi lại
  • Diffing algorithm tìm thay đổi tối thiểu

Conditional Rendering

function UserStatus({ isLoggedIn, user }) {
  // 1. if-else với early return
  if (!isLoggedIn) {
    return <div>Please log in</div>;
  }

  // 2. Ternary operator
  return (
    <div>
      {isLoggedIn ? (
        <span>Welcome, {user.name}!</span>
      ) : (
        <span>Guest</span>
      )}
    </div>
  );
}

// 3. && short-circuit (hiển thị hoặc không)
function Notification({ hasMessage, message }) {
  return (
    <div>
      <h1>Dashboard</h1>
      {hasMessage && <div className="alert">{message}</div>}
    </div>
  );
}

// 4. Nullish coalescing ??
function UserAvatar({ avatarUrl }) {
  return (
    <img src={avatarUrl ?? '/default-avatar.png'} alt="avatar" />
  );
}

// 5. Switch statement qua object map
const statusComponents = {
  loading: <Spinner />,
  error: <ErrorMessage />,
  success: <DataTable />,
};

function DataView({ status }) {
  return statusComponents[status] ?? <div>Unknown status</div>;
}

List Rendering

// Luôn cần key prop khi render list
function ProductList({ products }) {
  return (
    <ul>
      {products.map((product) => (
        <li key={product.id}>
          <span>{product.name}</span>
          <span>${product.price}</span>
        </li>
      ))}
    </ul>
  );
}

// Key phải là unique và stable
// ✅ Dùng id từ data
<li key={item.id}>

// ❌ Tránh dùng index nếu list có thể thay đổi thứ tự
<li key={index}>  // Gây bug khi sort/filter

// Render nested lists
function CategoryList({ categories }) {
  return (
    <div>
      {categories.map((category) => (
        <div key={category.id}>
          <h3>{category.name}</h3>
          <ul>
            {category.items.map((item) => (
              <li key={item.id}>{item.name}</li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}

// Kết hợp filter và map
function ActiveUsers({ users }) {
  return (
    <ul>
      {users
        .filter((user) => user.isActive)
        .map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
    </ul>
  );
}

Fragments

import { Fragment } from 'react';

// Cú pháp đầy đủ
function TableRow({ item }) {
  return (
    <Fragment>
      <td>{item.name}</td>
      <td>{item.value}</td>
    </Fragment>
  );
}

// Short syntax <> - không hỗ trợ key
function List({ items }) {
  return (
    <>
      {items.map((item) => (
        // Phải dùng Fragment khi cần key
        <Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </Fragment>
      ))}
    </>
  );
}

React 18 - Concurrent Rendering

import { startTransition, useDeferredValue } from 'react';

// startTransition - đánh dấu update không khẩn cấp
function SearchInput() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);

  function handleChange(e) {
    // Update UI ngay lập tức (khẩn cấp)
    setQuery(e.target.value);

    // Update kết quả tìm kiếm (không khẩn cấp)
    startTransition(() => {
      setResults(filterData(e.target.value));
    });
  }

  return (
    <>
      <input value={query} onChange={handleChange} />
      <SearchResults results={results} />
    </>
  );
}

// useDeferredValue - defer một value
function ProductSearch({ query }) {
  const deferredQuery = useDeferredValue(query);

  return <ProductList query={deferredQuery} />;
}