Back to blog

Phase 1: Nền Tảng React — Components, Props, State & Hooks

reactjavascriptfrontendcomponentshooks
Phase 1: Nền Tảng React — Components, Props, State & Hooks

Chào mừng đến với Phase 1 của series React & Next.js! Bài viết này dạy bạn các thành phần cốt lõi của React — những khái niệm bạn sẽ sử dụng trong mọi ứng dụng React từ nay về sau.

Chúng ta sẽ thực hành ngay lập tức. Đến cuối bài, bạn sẽ hiểu tại sao React ra đời, cách tư duy theo component, và cách quản lý state và side effects với hooks.

Thời gian cam kết: 5–7 ngày, 1–2 giờ mỗi ngày Điều kiện tiên quyết: JavaScript cơ bản (variables, functions, arrays, objects, promises)

Những Gì Bạn Sẽ Học

Sau khi hoàn thành Phase 1, bạn có thể:

✅ Giải thích React là gì và tại sao nó được tạo ra
✅ Viết JSX và hiểu cách nó ánh xạ sang JavaScript
✅ Xây dựng các components có thể tái sử dụng và kết hợp chúng
✅ Truyền dữ liệu giữa components bằng props
✅ Quản lý local UI state với useState
✅ Xử lý side effects với useEffect
✅ Xử lý events và form inputs trong React
✅ Render lists hiệu quả với keys
✅ Áp dụng các pattern conditional rendering


Thiết Lập Môi Trường

Bạn không cần cấu hình bất cứ điều gì để bắt đầu học React. Hãy dùng một trong các tùy chọn sau:

Tùy chọn A: Playground trực tuyến (không cần setup)

Truy cập react.new — một sandbox React sẵn sàng dùng ngay trên trình duyệt.

Tùy chọn B: Project cục bộ với Vite (khuyến nghị để học)

npm create vite@latest react-fundamentals -- --template react
cd react-fundamentals
npm install
npm run dev

Mở http://localhost:5173 và bạn đã chạy React.

Ở phần sau của series (Bài RN-4), bạn sẽ chuyển sang Next.js. Hiện tại, Vite là cách nhanh nhất để chạy React thuần trên máy cục bộ.


React Là Gì Và Tại Sao Nó Ra Đời?

Trước khi viết code, hãy hiểu vấn đề mà React giải quyết.

Vấn Đề: Đồng Bộ UI với Dữ Liệu

Hãy tưởng tượng một giỏ hàng. Khi người dùng thêm một sản phẩm, biểu tượng giỏ hàng phải cập nhật số lượng, panel giỏ hàng phải hiển thị sản phẩm mới, và tổng giá phải tính lại — tất cả cùng một lúc.

Với JavaScript thuần, bạn sẽ phải tìm từng phần tử DOM và cập nhật thủ công. Điều này trở thành cơn ác mộng bảo trì khi ứng dụng phát triển. Đồng bộ UI với dữ liệu thay đổi là thách thức cốt lõi của phát triển frontend.

Giải Pháp của React: Declarative UI

React lật ngược mô hình. Thay vì cập nhật DOM theo từng bước, bạn mô tả UI trông như thế nào với một state cụ thể, và React tự xử lý các cập nhật DOM.

// Imperative (JS thuần) — bạn quản lý DOM
document.getElementById('count').textContent = count;
document.getElementById('cart').classList.add('visible');
 
// Declarative (React) — bạn mô tả kết quả
function Cart({ items }) {
  return (
    <div className={items.length > 0 ? 'cart visible' : 'cart'}>
      <span>{items.length}</span>
    </div>
  );
}

Khi items thay đổi, React tự động render lại Cart. Bạn không cần chạm vào DOM.

Virtual DOM

React duy trì một bản sao nhẹ của DOM trong bộ nhớ — Virtual DOM. Khi state thay đổi, React:

  1. Render lại component thành cây Virtual DOM mới
  2. So sánh với cây cũ (reconciliation)
  3. Chỉ áp dụng những thay đổi tối thiểu lên real DOM

Điều này nhanh. Và quan trọng hơn, nó cho phép bạn viết code declarative mà không cần nghĩ đến DOM manipulation.


JSX: HTML trong JavaScript

JSX là cú pháp React dùng để mô tả UI. Nó trông giống HTML nhưng compile thành các lời gọi hàm JavaScript thông thường.

// JSX
const element = <h1 className="title">Xin chào, React!</h1>;
 
// Nó compile thành (bạn không bao giờ viết cái này)
const element = React.createElement('h1', { className: 'title' }, 'Xin chào, React!');

Các Quy Tắc JSX

1. Trả về một phần tử gốc duy nhất

// ❌ Hai phần tử anh em — không hợp lệ
return (
  <h1>Tiêu đề</h1>
  <p>Nội dung</p>
);
 
// ✅ Bọc trong một div
return (
  <div>
    <h1>Tiêu đề</h1>
    <p>Nội dung</p>
  </div>
);
 
// ✅ Hoặc dùng Fragment (không tạo thêm node DOM)
return (
  <>
    <h1>Tiêu đề</h1>
    <p>Nội dung</p>
  </>
);

2. Dùng className thay vì class

// ❌ Attribute HTML
<div class="container">
 
// ✅ Attribute JSX
<div className="container">

3. Tự đóng các thẻ không có children

<img src="photo.jpg" alt="Ảnh" />
<input type="text" />
<br />

4. Biểu thức JavaScript nằm trong {}

const name = "Alice";
const age = 30;
 
return (
  <div>
    <h1>Xin chào, {name}!</h1>
    <p>Tuổi: {age}</p>
    <p>Năm sinh: {new Date().getFullYear() - age}</p>
    <p>{age >= 18 ? 'Người lớn' : 'Trẻ vị thành niên'}</p>
  </div>
);

5. Attributes dùng camelCase

<input
  type="text"
  onChange={handleChange}
  autoComplete="off"
  tabIndex={1}
/>

Components: Các Khối Xây Dựng

Một React component là một hàm JavaScript trả về JSX. Components là đơn vị cốt lõi của React — mọi thứ đều là component.

Component Đầu Tiên Của Bạn

function Greeting() {
  return <h1>Xin chào, thế giới!</h1>;
}

Đó là tất cả. Một component chỉ là một hàm. React gọi nó và đặt JSX được trả về vào DOM.

Quy Tắc Component

  • Tên phải bắt đầu bằng chữ hoa — Greeting, không phải greeting. React dùng điều này để phân biệt components với HTML tags.
  • Phải trả về JSX (hoặc null để không render gì)
  • Có thể được dùng như HTML tags trong các components khác

Kết Hợp Components

Components lồng vào nhau, giống như các phần tử HTML:

function Avatar() {
  return <img src="/avatar.png" alt="Ảnh đại diện" className="avatar" />;
}
 
function UserBio() {
  return (
    <div className="bio">
      <p>Lập trình viên. Mê cà phê.</p>
    </div>
  );
}
 
function UserCard() {
  return (
    <div className="card">
      <Avatar />
      <UserBio />
    </div>
  );
}
 
function App() {
  return (
    <main>
      <h1>Thành Viên Nhóm</h1>
      <UserCard />
      <UserCard />
      <UserCard />
    </main>
  );
}

Đây là cây component React — toàn bộ UI của bạn là một cây components.


Props: Truyền Dữ Liệu cho Components

Props (viết tắt của properties) cho phép bạn truyền dữ liệu từ component cha xuống con. Chúng giúp components có thể tái sử dụng.

Props Cơ Bản

function Greeting({ name, role }) {
  return (
    <div>
      <h2>Xin chào, {name}!</h2>
      <p>Vai trò: {role}</p>
    </div>
  );
}
 
function App() {
  return (
    <div>
      <Greeting name="Alice" role="Admin" />
      <Greeting name="Bob" role="Biên tập viên" />
      <Greeting name="Carol" role="Người xem" />
    </div>
  );
}

Props Mặc Định

function Button({ label, variant = 'primary', disabled = false }) {
  return (
    <button className={`btn btn-${variant}`} disabled={disabled}>
      {label}
    </button>
  );
}
 
// Dùng giá trị mặc định
<Button label="Lưu" />
 
// Ghi đè giá trị mặc định
<Button label="Xóa" variant="danger" disabled={true} />

Prop children

Bất kỳ JSX nào được đặt giữa thẻ mở và đóng của component sẽ trở thành prop children:

function Card({ children, title }) {
  return (
    <div className="card">
      <h3 className="card-title">{title}</h3>
      <div className="card-body">{children}</div>
    </div>
  );
}
 
// Cách dùng
<Card title="Hồ Sơ">
  <p>Tên: Alice</p>
  <p>Email: [email protected]</p>
</Card>

Props Là Read-Only

Không bao giờ sửa đổi props bên trong component. Props chỉ chảy một chiều — từ cha xuống con. Một component chỉ có thể đọc props, không bao giờ thay đổi chúng.

// ❌ Không bao giờ làm điều này
function BadComponent({ count }) {
  count = count + 1; // Thay đổi props — sai!
  return <p>{count}</p>;
}
 
// ✅ Dùng state cho các giá trị có thể thay đổi
function GoodComponent({ initialCount }) {
  const [count, setCount] = useState(initialCount);
  return <p>{count}</p>;
}

State: Làm Components Trở Nên Reactive

Props là dữ liệu bên ngoài được truyền vào. State là dữ liệu nội bộ mà component sở hữu và có thể thay đổi. Khi state thay đổi, React render lại component.

useState

import { useState } from 'react';
 
function Counter() {
  const [count, setCount] = useState(0); // Giá trị ban đầu: 0
 
  return (
    <div>
      <p>Đếm: {count}</p>
      <button onClick={() => setCount(count + 1)}>Tăng</button>
      <button onClick={() => setCount(count - 1)}>Giảm</button>
      <button onClick={() => setCount(0)}>Đặt lại</button>
    </div>
  );
}

useState trả về một mảng với hai phần tử:

  1. Giá trị state hiện tại (count)
  2. Hàm setter (setCount) — gọi hàm này để cập nhật giá trị

Cập Nhật State Là Bất Đồng Bộ

React gộp các cập nhật state để tối ưu hiệu năng. Không bao giờ dựa vào state được cập nhật ngay lập tức sau khi gọi setter:

// ❌ State cũ — count có thể không phản ánh giá trị mới nhất
function Counter() {
  const [count, setCount] = useState(0);
 
  function handleTripleClick() {
    setCount(count + 1); // Cả ba đều dùng count cũ
    setCount(count + 1);
    setCount(count + 1);
    // Kết quả: count chỉ tăng từ 0 lên 1, không phải 3
  }
}
 
// ✅ Functional update — luôn dùng state mới nhất
function Counter() {
  const [count, setCount] = useState(0);
 
  function handleTripleClick() {
    setCount(prev => prev + 1); // Dùng giá trị mới nhất mỗi lần
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
    // Kết quả: count tăng từ 0 lên 3 ✅
  }
}

State Object

function ProfileForm() {
  const [user, setUser] = useState({
    name: '',
    email: '',
    age: 0,
  });
 
  return (
    <form>
      <input
        value={user.name}
        onChange={e => setUser(prev => ({ ...prev, name: e.target.value }))}
        placeholder="Tên"
      />
      <input
        value={user.email}
        onChange={e => setUser(prev => ({ ...prev, email: e.target.value }))}
        placeholder="Email"
      />
    </form>
  );
}

Đặt State Ở Đâu

Kỹ năng React quan trọng là quyết định component nào nên sở hữu từng phần state.

Quy tắc: Đặt state ở tổ tiên chung thấp nhất của tất cả components cần nó.

// ❌ State bị nhân đôi ở hai components — sẽ không đồng bộ
function ComponentA() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
 
function ComponentB() {
  const [count, setCount] = useState(0); // Count khác!
  return <p>Đếm: {count}</p>;
}
 
// ✅ State được nâng lên component cha — nguồn sự thật duy nhất
function Parent() {
  const [count, setCount] = useState(0);
  return (
    <>
      <ComponentA count={count} onIncrement={() => setCount(c => c + 1)} />
      <ComponentB count={count} />
    </>
  );
}

Pattern này được gọi là lifting state up — nó là nền tảng của kiến trúc React.


Xử Lý Events

React xử lý events với attributes camelCase và tham chiếu hàm (không phải chuỗi):

// ❌ Kiểu HTML
<button onclick="handleClick()">Nhấn vào đây</button>
 
// ✅ Kiểu React
<button onClick={handleClick}>Nhấn vào đây</button>

Các Pattern Event Handler

function EventExamples() {
  // Arrow function inline
  const handleClick = () => console.log('Đã nhấn!');
 
  // Với event object
  const handleInput = (event) => {
    console.log(event.target.value);
  };
 
  // Truyền đối số
  const handleItemClick = (id) => {
    console.log('Đã nhấn vào item:', id);
  };
 
  return (
    <div>
      <button onClick={handleClick}>Nhấn vào đây</button>
      <input onChange={handleInput} />
 
      {/* ✅ Arrow function để truyền đối số */}
      <button onClick={() => handleItemClick(42)}>Item 42</button>
 
      {/* ❌ Cái này gọi handleItemClick ngay khi render! */}
      <button onClick={handleItemClick(42)}>Sai!</button>
    </div>
  );
}

Controlled Inputs

Các form trong React dùng controlled inputs — giá trị input được điều khiển bởi state:

function SearchBar() {
  const [query, setQuery] = useState('');
 
  const handleSubmit = (e) => {
    e.preventDefault(); // Ngăn tải lại trang
    console.log('Tìm kiếm:', query);
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={query}                          // Được điều khiển bởi state
        onChange={e => setQuery(e.target.value)} // Cập nhật state
        placeholder="Tìm kiếm..."
      />
      <button type="submit">Tìm</button>
      <p>Từ khóa: {query}</p>
    </form>
  );
}

Conditional Rendering

React không có cú pháp template như v-if hay *ngIf. Bạn dùng JavaScript thông thường:

Toán Tử Ternary

function UserStatus({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <p>Chào mừng trở lại!</p>
      ) : (
        <p>Vui lòng đăng nhập.</p>
      )}
    </div>
  );
}

Logical AND (&&)

Render gì đó chỉ khi điều kiện đúng:

function Notification({ hasMessages, messageCount }) {
  return (
    <div>
      <h1>Hộp thư</h1>
      {hasMessages && (
        <p>Bạn có {messageCount} tin nhắn chưa đọc.</p>
      )}
    </div>
  );
}

Lưu ý: 0 && <Component /> render số 0, không phải không có gì! Dùng !!count && ... hoặc ternary thay thế.

Early Return

Với điều kiện phức tạp, return sớm giúp components dễ đọc hơn:

function UserDashboard({ user, isLoading, error }) {
  if (isLoading) return <p>Đang tải...</p>;
  if (error) return <p>Lỗi: {error.message}</p>;
  if (!user) return null;
 
  return (
    <div>
      <h1>Chào mừng, {user.name}</h1>
      {/* ... nội dung dashboard */}
    </div>
  );
}

Render Lists

Dùng .map() để chuyển đổi mảng thành các phần tử JSX:

const fruits = ['Táo', 'Chuối', 'Cherry'];
 
function FruitList() {
  return (
    <ul>
      {fruits.map(fruit => (
        <li key={fruit}>{fruit}</li>
      ))}
    </ul>
  );
}

Prop key

Mỗi item trong list cần một prop key duy nhất. React dùng keys để theo dõi những item nào đã thay đổi, được thêm vào, hoặc bị xóa.

const users = [
  { id: 1, name: 'Alice', role: 'Admin' },
  { id: 2, name: 'Bob', role: 'Biên tập viên' },
  { id: 3, name: 'Carol', role: 'Người xem' },
];
 
function UserList() {
  return (
    <ul>
      {users.map(user => (
        // ✅ Dùng ID ổn định và duy nhất làm key
        <li key={user.id}>
          {user.name} — {user.role}
        </li>
      ))}
    </ul>
  );
}

Quy tắc key:

  • Keys phải duy nhất trong các phần tử anh em (không cần toàn cục)
  • Dùng IDs ổn định từ dữ liệu của bạn (database IDs, slugs, v.v.)
  • Không bao giờ dùng index mảng làm key khi list có thể sắp xếp lại hoặc items có thể được thêm/xóa — nó gây ra các lỗi tinh vi
// ❌ Index làm key — lỗi khi list sắp xếp lại
{items.map((item, index) => (
  <Item key={index} data={item} />
))}
 
// ✅ ID ổn định làm key
{items.map(item => (
  <Item key={item.id} data={item} />
))}

useEffect: Quản Lý Side Effects

Components nên thuần túy — với cùng props và state, chúng luôn trả về cùng JSX. Nhưng ứng dụng thực cần side effects: fetch dữ liệu, thiết lập subscriptions, cập nhật document title.

useEffect xử lý tất cả những điều này.

Cách Dùng Cơ Bản

import { useState, useEffect } from 'react';
 
function DocumentTitle({ page }) {
  useEffect(() => {
    document.title = `Trang: ${page}`;
  }, [page]); // Chạy khi `page` thay đổi
 
  return <h1>Trang hiện tại: {page}</h1>;
}

Mảng Dependency

Đối số thứ hai của useEffect kiểm soát khi nào nó chạy:

// ✅ Chạy sau mỗi lần render
useEffect(() => {
  console.log('Đã render');
});
 
// ✅ Chạy một lần, chỉ khi mount
useEffect(() => {
  console.log('Đã mount');
}, []);
 
// ✅ Chạy khi `userId` hoặc `filter` thay đổi
useEffect(() => {
  fetchUser(userId, filter);
}, [userId, filter]);

Fetch Dữ Liệu

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
 
  useEffect(() => {
    setLoading(true);
    setError(null);
 
    fetch(`https://api.example.com/users/${userId}`)
      .then(res => {
        if (!res.ok) throw new Error('Không tìm thấy người dùng');
        return res.json();
      })
      .then(data => {
        setUser(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setLoading(false);
      });
  }, [userId]); // Fetch lại khi userId thay đổi
 
  if (loading) return <p>Đang tải...</p>;
  if (error) return <p>Lỗi: {error}</p>;
 
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

Cleanup

Một số effects cần cleanup — để tránh memory leaks và stale updates:

function Timer() {
  const [seconds, setSeconds] = useState(0);
 
  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);
 
    // ✅ Cleanup: xóa interval khi component unmount
    return () => clearInterval(interval);
  }, []); // Mảng rỗng — thiết lập một lần, dọn dẹp khi unmount
 
  return <p>Thời gian trôi qua: {seconds}s</p>;
}
function SearchResults({ query }) {
  const [results, setResults] = useState([]);
 
  useEffect(() => {
    let cancelled = false; // Flag để ngăn stale updates
 
    fetch(`/api/search?q=${query}`)
      .then(res => res.json())
      .then(data => {
        if (!cancelled) setResults(data); // Chỉ cập nhật nếu vẫn còn liên quan
      });
 
    return () => { cancelled = true; }; // Cleanup khi render tiếp theo
  }, [query]);
 
  return <ul>{results.map(r => <li key={r.id}>{r.title}</li>)}</ul>;
}

Các Lỗi useEffect Phổ Biến

// ❌ Thiếu dependency — stale closure
useEffect(() => {
  fetchData(userId); // userId được dùng nhưng không có trong deps array
}, []); // Lỗi: không bao giờ fetch lại khi userId thay đổi
 
// ✅ Thêm tất cả dependencies
useEffect(() => {
  fetchData(userId);
}, [userId]);
 
// ❌ Object trong dependency array — gây vòng lặp vô hạn
useEffect(() => {
  fetchData(options);
}, [options]); // options là object mới mỗi lần render!
 
// ✅ Dùng giá trị primitive hoặc chuyển object vào trong effect
useEffect(() => {
  const options = { limit: 10, page: currentPage };
  fetchData(options);
}, [currentPage]);

Kết Hợp Tất Cả: Quản Lý Task

Hãy xây dựng một trình quản lý task nhỏ kết hợp mọi thứ từ bài này:

import { useState, useEffect } from 'react';
 
function TaskItem({ task, onToggle, onDelete }) {
  return (
    <li className={task.done ? 'done' : ''}>
      <input
        type="checkbox"
        checked={task.done}
        onChange={() => onToggle(task.id)}
      />
      <span>{task.text}</span>
      <button onClick={() => onDelete(task.id)}>Xóa</button>
    </li>
  );
}
 
function TaskForm({ onAdd }) {
  const [text, setText] = useState('');
 
  const handleSubmit = (e) => {
    e.preventDefault();
    if (!text.trim()) return;
    onAdd(text.trim());
    setText('');
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
        placeholder="Thêm task..."
      />
      <button type="submit">Thêm</button>
    </form>
  );
}
 
function TaskManager() {
  const [tasks, setTasks] = useState([]);
  const [filter, setFilter] = useState('all'); // 'all' | 'active' | 'done'
 
  // Lưu tasks vào localStorage
  useEffect(() => {
    const saved = localStorage.getItem('tasks');
    if (saved) setTasks(JSON.parse(saved));
  }, []);
 
  useEffect(() => {
    localStorage.setItem('tasks', JSON.stringify(tasks));
  }, [tasks]);
 
  const addTask = (text) => {
    setTasks(prev => [
      ...prev,
      { id: Date.now(), text, done: false },
    ]);
  };
 
  const toggleTask = (id) => {
    setTasks(prev =>
      prev.map(t => t.id === id ? { ...t, done: !t.done } : t)
    );
  };
 
  const deleteTask = (id) => {
    setTasks(prev => prev.filter(t => t.id !== id));
  };
 
  const filteredTasks = tasks.filter(t => {
    if (filter === 'active') return !t.done;
    if (filter === 'done') return t.done;
    return true;
  });
 
  return (
    <div className="task-manager">
      <h1>Tasks ({tasks.filter(t => !t.done).length} còn lại)</h1>
 
      <TaskForm onAdd={addTask} />
 
      <div className="filters">
        {[
          { key: 'all', label: 'Tất cả' },
          { key: 'active', label: 'Chưa xong' },
          { key: 'done', label: 'Đã xong' },
        ].map(f => (
          <button
            key={f.key}
            onClick={() => setFilter(f.key)}
            className={filter === f.key ? 'active' : ''}
          >
            {f.label}
          </button>
        ))}
      </div>
 
      {filteredTasks.length === 0 ? (
        <p>Không có task nào.</p>
      ) : (
        <ul>
          {filteredTasks.map(task => (
            <TaskItem
              key={task.id}
              task={task}
              onToggle={toggleTask}
              onDelete={deleteTask}
            />
          ))}
        </ul>
      )}
    </div>
  );
}
 
export default TaskManager;

Ví dụ đơn này minh họa:

  • Nhiều components (TaskItem, TaskForm, TaskManager)
  • Truyền props và children
  • useState cho tasks, filter và form input
  • useEffect để lưu localStorage
  • Controlled inputs
  • Event handlers
  • Render list với keys
  • Conditional rendering

Tư Duy React Developer

Trước khi chuyển sang Phase 2, hãy thấm nhuần các pattern tư duy sau:

Tư Duy Theo Component

Phân tách bất kỳ UI nào thành cây components. Tự hỏi:

  • Component này cần dữ liệu gì? (props)
  • Component này sở hữu và thay đổi gì? (state)
  • Tôi có thể trích xuất gì thành component nhỏ hơn, tái sử dụng được?

Dữ Liệu Chảy Xuống, Events Chảy Lên

Dữ liệu chảy xuống qua props. Thay đổi chảy lên qua các callback function được truyền dưới dạng props. Không bao giờ ngược lại.

Giữ Components Thuần Túy

Output của component chỉ nên phụ thuộc vào props và state của nó — không có dependency ẩn, không có giá trị ngẫu nhiên, không trực tiếp thay đổi dữ liệu bên ngoài.

State Là Một Snapshot

Khi bạn gọi setCount(count + 1), React không thay đổi count ngay lập tức. Nó lên lịch re-render. Lần tiếp theo component render, nó nhận được count mới. Hãy nghĩ state như một snapshot của một khoảnh khắc.


Bài Tập Thực Hành

Làm qua các bài tập này để củng cố các khái niệm Phase 1:

Bài tập 1 — Toggle Xây dựng component với button toggle giữa hiển thị và ẩn đoạn văn bản.

Bài tập 2 — Bộ đếm với bước nhảy Xây dựng bộ đếm nơi người dùng có thể đặt số bước nhảy (1, 5, 10) và tăng/giảm theo bước đó.

Bài tập 3 — Danh sách mua sắm Xây dựng danh sách mua sắm nơi người dùng có thể thêm items, đánh dấu hoàn thành và xóa. Hiển thị số lượng items chưa đánh dấu.

Bài tập 4 — Fetch & Hiển thị Fetch danh sách bài đăng từ https://jsonplaceholder.typicode.com/posts và hiển thị chúng. Hiển thị trạng thái loading khi đang fetch và trạng thái lỗi nếu request thất bại.

Bài tập 5 — Color Picker Xây dựng component với ba sliders (R, G, B, 0–255). Hiển thị ô màu cập nhật theo thời gian thực khi sliders thay đổi.


Tóm Tắt

Trong Phase 1, bạn đã học các khái niệm React nền tảng:

Khái NiệmLàm Gì
JSXCú pháp HTML-like compile thành các lời gọi React.createElement
ComponentsHàm trả về JSX — các khối xây dựng của React UI
PropsDữ liệu read-only được truyền từ cha xuống con
State (useState)Dữ liệu nội bộ có thể thay đổi — thay đổi kích hoạt re-renders
useEffectChạy side effects (fetch, timers, subscriptions) sau khi render
EventsXử lý tương tác người dùng với event attributes camelCase
Lists & KeysRender mảng với .map(), dùng keys ổn định
Conditional renderingTernary, &&, hoặc early return cho UI có điều kiện

Các khái niệm này là nền tảng hoàn chỉnh. Mọi pattern React nâng cao đều xây dựng trên chúng.


Tiếp Theo Là Gì

Trong Bài RN-3: Phase 2 — Hệ Sinh Thái React, bạn sẽ mở rộng nền tảng này với:

  • Client-side routing với React Router
  • Global state management
  • Form libraries (React Hook Form)
  • Data fetching với SWR hoặc TanStack Query
  • Component libraries (Tailwind CSS, shadcn/ui)

Chỉ Mục Series

BàiTiêu ĐềTrạng Thái
RN-1React.js & Next.js Roadmap✅ Hoàn thành
RN-2Phase 1: Nền Tảng React✅ Bạn đang ở đây
RN-3Phase 2: Hệ Sinh Thái ReactSắp Ra Mắt
RN-4Phase 3: Next.js Full-StackSắp Ra Mắt
RN-5Deep Dive: Thành Thạo React HooksSắp Ra Mắt
RN-6Deep Dive: State ManagementSắp Ra Mắt
RN-7Deep Dive: App Router & Server ComponentsSắp Ra Mắt
RN-8Deep Dive: Các Pattern Data FetchingSắp Ra Mắt
RN-9Deep Dive: Tối Ưu Hiệu NăngSắp Ra Mắt
RN-10Deep Dive: Authentication trong Next.jsSắp Ra Mắt
RN-11Deep Dive: Testing React & Next.jsSắp Ra Mắt
RN-12Deep Dive: Deploy Ứng Dụng Next.jsSắp Ra Mắt

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.