Cơ bảnKiến thức cơ bản

Props và State trong React: Khác nhau như thế nào?

8 phút đọc0 lượt xem
#props state react#props là gì react#useState react#truyền dữ liệu react#state management react#lifting state up#props children react

Props và State trong React là gì? Tại sao cần phân biệt?

Khi mới học React, nhiều bạn thường gặp phải câu hỏi quen thuộc: "Dữ liệu này để vào props hay state?" Nghe có vẻ đơn giản, nhưng nếu trả lời sai, component của bạn sẽ ngày càng phức tạp, khó bảo trì và sinh ra nhiều lỗi khó tìm.

Tin tốt là: một khi hiểu rõ sự khác biệt, bạn sẽ viết React tự tin hơn rất nhiều. Bài này sẽ giải thích Props và State qua ví dụ thực tế và code JSX cụ thể.

Nếu bạn chưa biết React là gì, hãy đọc trước bài React là gì? rồi quay lại đây nhé.

Props là gì trong React?

Props (viết tắt của "properties") là cơ chế truyền dữ liệu một chiều từ component cha (parent) xuống component con (child). Dữ liệu chỉ chảy theo một hướng: từ trên xuống dưới.

Ví von dễ hiểu: Hãy hình dung bạn là quản lý (component cha) đưa phiếu đặt hàng cho nhân viên (component con). Nhân viên làm việc theo phiếu đó, nhưng không được tự ý sửa nội dung phiếu. Đó chính là Props.

Xem thêm về cách tổ chức component trong bài Component trong React.

Ví dụ cơ bản về Props

// Component cha
function App() {
  return <UserCard name="Minh" age={22} isStudent={true} />;
}

// Component con nhận props
function UserCard({ name, age, isStudent }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Tuổi: {age}</p>
      <p>{isStudent ? "Sinh viên" : "Đã đi làm"}</p>
    </div>
  );
}

Props có thể là bất kỳ kiểu dữ liệu nào: string, number, boolean, array, object, hoặc thậm chí một function.

Props là read-only – Không được sửa

Đây là quy tắc bất di bất dịch của React: component con không được phép thay đổi giá trị props mà nó nhận được.

// ❌ SAI – Không bao giờ làm điều này
function UserCard({ name }) {
  name = "Tên khác"; // Vi phạm nguyên tắc React!
  return <p>{name}</p>;
}

Nếu cần thay đổi dữ liệu, hãy dùng State ở component cha, rồi truyền hàm cập nhật xuống dưới qua props.

Default Props – Giá trị mặc định

function Button({ label = "Nhấn vào đây", color = "blue" }) {
  return (
    <button style={{ background: color }}>
      {label}
    </button>
  );
}

// Dùng không cần truyền props – sẽ dùng giá trị mặc định
// <Button /> → nút màu blue với chữ "Nhấn vào đây"

props.children – Truyền nội dung JSX

props.children là một props đặc biệt, chứa tất cả JSX nằm giữa thẻ mở và thẻ đóng của component. Đây là pattern rất phổ biến để tạo layout component.

function Card({ children }) {
  return (
    <div style={{ border: "1px solid #ddd", padding: 16, borderRadius: 8 }}>
      {children}
    </div>
  );
}

// Sử dụng – đặt bất kỳ nội dung nào vào bên trong
<Card>
  <h2>Tiêu đề bài viết</h2>
  <p>Nội dung bài viết nằm trong Card.</p>
</Card>

Pattern này được dùng nhiều trong Modal, Tooltip, Layout wrapper, và các UI component tổng quát.

State là gì trong React?

Statetrạng thái nội bộ của component – dữ liệu mà component tự quản lý và có thể thay đổi theo thời gian. Mỗi khi state thay đổi, React sẽ tự động re-render component để cập nhật giao diện.

Ví von: Giống như màn hình âm lượng trên TV. Số âm lượng do TV tự quản lý nội bộ, khi bạn nhấn tăng/giảm thì số thay đổi và màn hình cập nhật ngay lập tức.

useState Hook – Khai báo State

Trong React function component (cách viết hiện đại), bạn dùng hook useState để khai báo state:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // Giá trị ban đầu là 0

  return (
    <div>
      <p>Số đếm: {count}</p>
      <button onClick={() => setCount(count + 1)}>Tăng</button>
      <button onClick={() => setCount(count - 1)}>Giảm</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}

useState trả về một mảng gồm hai phần tử:

  • count – giá trị state hiện tại
  • setCount – hàm để cập nhật state (và trigger re-render)

Tìm hiểu thêm về các hook khác trong bài React Hooks là gì?

So sánh Props và State

Đây là bảng so sánh trực quan để bạn nhớ ngay sự khác biệt:

Tiêu chí Props State
Nguồn gốc dữ liệu Từ component cha Nội bộ component
Ai quản lý? Component cha Chính component đó
Có thể thay đổi? Không (read-only) Có (qua hàm setState)
Trigger re-render Khi parent re-render Khi gọi setState
Dùng để làm gì? Truyền dữ liệu/config xuống Lưu trạng thái UI
Ví dụ <Button color="red" /> const [open, setOpen] = useState(false)

Tóm gọn: Props là "dữ liệu nhận từ bên ngoài", State là "dữ liệu tự quản lý bên trong". Nếu component cần thay đổi dữ liệu → dùng State. Nếu chỉ cần hiển thị dữ liệu từ cha → dùng Props.

useState nâng cao: Mảng, Object và Form

State với Array – Danh sách Todo

Khi state là một mảng, không được dùng .push() hay các phương thức biến đổi trực tiếp. Thay vào đó, luôn tạo một mảng mới:

function TodoList() {
  const [todos, setTodos] = useState(["Học React", "Làm bài tập"]);
  const [input, setInput] = useState("");

  function addTodo() {
    if (!input.trim()) return;
    setTodos([...todos, input]); // Tạo mảng mới với spread operator
    setInput("");
  }

  function removeTodo(index) {
    setTodos(todos.filter((_, i) => i !== index));
  }

  return (
    <div>
      <input
        value={input}
        onChange={e => setInput(e.target.value)}
        placeholder="Thêm việc cần làm..."
      />
      <button onClick={addTodo}>Thêm</button>
      <ul>
        {todos.map((todo, i) => (
          <li key={i}>
            {todo} <button onClick={() => removeTodo(i)}>Xóa</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

State với Object – Form đăng ký

Khi state là một object, dùng spread operator để giữ lại các field khác khi cập nhật:

function SignupForm() {
  const [form, setForm] = useState({
    name: "",
    email: "",
    password: ""
  });

  function handleChange(e) {
    // Dùng computed property [e.target.name] để cập nhật đúng field
    setForm({ ...form, [e.target.name]: e.target.value });
  }

  function handleSubmit(e) {
    e.preventDefault();
    console.log("Dữ liệu form:", form);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="name"
        value={form.name}
        onChange={handleChange}
        placeholder="Họ tên"
      />
      <input
        name="email"
        type="email"
        value={form.email}
        onChange={handleChange}
        placeholder="Email"
      />
      <input
        name="password"
        type="password"
        value={form.password}
        onChange={handleChange}
        placeholder="Mật khẩu"
      />
      <button type="submit">Đăng ký</button>
    </form>
  );
}

Pattern { ...form, [e.target.name]: e.target.value } là cách chuẩn để cập nhật object state mà không làm mất các field còn lại.

Event Handler kết hợp State

Thực tế khi xây dựng UI, event handler (xử lý sự kiện) thường là nơi gọi hàm setState để cập nhật trạng thái:

function LikeButton() {
  const [liked, setLiked] = useState(false);
  const [count, setCount] = useState(0);

  function handleLike() {
    if (!liked) {
      setLiked(true);
      setCount(count + 1);
    } else {
      setLiked(false);
      setCount(count - 1);
    }
  }

  return (
    <button
      onClick={handleLike}
      style={{ color: liked ? "red" : "gray", fontSize: 18 }}
    >
      ♥ {count} Thích
    </button>
  );
}

Ví dụ này minh họa: một nút Like đơn giản quản lý 2 state (likedcount), cập nhật đồng thời khi người dùng nhấn.

Lifting State Up – Nâng State lên component cha

Khi nào cần dùng?

Khi hai hoặc nhiều component anh em (siblings) cần chia sẻ cùng một dữ liệu, giải pháp là đưa state lên component cha chung, rồi truyền xuống qua props và callback.

Ví dụ: Thanh tìm kiếm và danh sách kết quả

// Component cha giữ state
function SearchApp() {
  const [query, setQuery] = useState("");

  return (
    <div>
      <SearchInput value={query} onChange={setQuery} />
      <SearchResults query={query} />
    </div>
  );
}

// Component con 1: Input nhận callback để cập nhật state cha
function SearchInput({ value, onChange }) {
  return (
    <input
      value={value}
      onChange={e => onChange(e.target.value)}
      placeholder="Tìm kiếm bài viết..."
      style={{ width: "100%", padding: 8 }}
    />
  );
}

// Component con 2: Hiển thị kết quả dựa trên props
function SearchResults({ query }) {
  const allArticles = [
    "React là gì?",
    "Props và State trong React",
    "React Hooks cơ bản",
    "Component trong React"
  ];
  const results = allArticles.filter(a =>
    a.toLowerCase().includes(query.toLowerCase())
  );

  return (
    <ul>
      {results.length > 0
        ? results.map(r => <li key={r}>{r}</li>)
        : <li>Không tìm thấy kết quả</li>
      }
    </ul>
  );
}

SearchInputSearchResults là hai component anh em. Cả hai cần biết giá trị của query – một để hiển thị trong input, một để lọc danh sách. Giải pháp: đưa state query lên SearchApp (component cha chung).

Nguyên tắc: Đặt state ở component thấp nhất có thể. Chỉ "nâng lên" khi thực sự cần chia sẻ giữa nhiều component.

3 Lỗi phổ biến khi dùng State

Lỗi 1: Thay đổi State trực tiếp (Mutate)

// ❌ SAI – Không trigger re-render
const [items, setItems] = useState([1, 2, 3]);
items.push(4); // Sai! Biến đổi trực tiếp
setItems(items); // React không biết items đã thay đổi

// ✅ ĐÚNG – Tạo mảng mới
setItems([...items, 4]);

Lỗi 2: Quên spread operator khi cập nhật Object

// ❌ SAI – Mất các field khác (email, password biến mất)
setForm({ name: "Minh" });

// ✅ ĐÚNG – Giữ nguyên các field khác
setForm({ ...form, name: "Minh" });

Lỗi 3: Đọc State ngay sau khi gọi setState

// ❌ SAI – count vẫn là giá trị cũ
setCount(count + 1);
console.log(count); // Vẫn in ra giá trị CŨ

// ✅ ĐÚNG – Dùng functional update nếu cần giá trị mới nhất
setCount(prev => prev + 1);

State update là bất đồng bộ – React batch nhiều update lại rồi mới re-render một lần để tối ưu hiệu năng.

Tổng kết

Props và State là nền tảng của mọi ứng dụng React. Nắm vững hai khái niệm này giúp bạn thiết kế component logic, dễ bảo trì và dễ mở rộng.

  • Props: Truyền từ cha xuống con, read-only, dùng để cấu hình component
  • State: Nội bộ component, thay đổi được, dùng để quản lý trạng thái UI
  • useState: Hook để khai báo state – dùng cho number, string, boolean, array, object
  • Lifting State Up: Khi 2 component anh em cần share data → đưa state lên cha chung

Bước tiếp theo: Học thêm về React Hooks để khám phá useEffect, useContext và các hook mạnh mẽ khác – giúp bạn xây dựng ứng dụng React hoàn chỉnh.

Về tác giả

Ảnh đại diện tác giả Kenji — họa tiết hình học

Kenji

Kỹ sư phần mềm full-stack (Web), hơn 5 năm kinh nghiệm thực tế

  • Python
  • DB
  • Hạ tầng
  • Đào tạo & cố vấn
  • AI

Làm việc cùng đồng nghiệp người Việt, tôi thấy thiếu tài liệu kỹ thuật rõ ràng bằng tiếng Việt. codeahoc là nơi tôi chia sẻ theo hướng thực tế, dễ áp dụng.

Nguyên tắc nội dung

  • Ưu tiên nguồn gốc và góc nhìn từ thực tế triển khai.
  • Nếu có sai sót, nội dung sẽ được cập nhật và sửa kịp thời.

Khóa học liên quan

React - The Complete Guide (incl. React Router & Redux)

Làm chủ React.js với hooks, Redux và dự án thực tế.

4.6499.000 ₫
Xem khóa học →

The Complete Web Developer Bootcamp

HTML, CSS, JavaScript và nền tảng trước khi đi sâu React.

4.7499.000 ₫
Xem khóa học →
Quảng cáo