Chào mừng bạn quay trở lại với chuỗi bài viết “React Roadmap – Lộ trình học React 2025“! Sau khi đã cùng nhau tìm hiểu React là gì, khám phá sự chuyển dịch từ Class Components sang Functional Components và “giải mã” bí ẩn của JSX, giờ là lúc chúng ta đi sâu vào hai khái niệm cốt lõi, là trái tim và linh hồn của việc quản lý dữ liệu trong các ứng dụng React: Props và State.
Nếu bạn đã từng cảm thấy bối rối về việc khi nào thì sử dụng Props, khi nào thì cần State, hay làm sao dữ liệu di chuyển trong cây component của bạn, thì bài viết này chính là dành cho bạn. Chúng ta sẽ cùng nhau làm sáng tỏ vai trò, cách hoạt động và quan trọng nhất là ai kiểm soát dữ liệu gì khi sử dụng Props và State.
Mục lục
Props: Người Truyền Tin Từ Cha Xuống Con
Hãy tưởng tượng các component của bạn như những khối lego. Để một khối lego (component con) có hình dạng, màu sắc hay chức năng cụ thể, nó cần được “cấu hình” từ khối lego lớn hơn (component cha) lắp ghép nó. Trong React, “cấu hình” này được truyền xuống thông qua Props (viết tắt của “properties” – thuộc tính).
Bản chất của Props:
- Từ ngoài vào: Props luôn được truyền từ component cha xuống component con. Component con nhận Props như một bộ tham số đầu vào.
- Bất biến (Immutable) đối với component nhận: Đây là một điểm cực kỳ quan trọng. Một khi một component nhận được Props, nó không được phép thay đổi giá trị của Props đó. Props là “read-only” (chỉ đọc) từ góc nhìn của component con. Nếu component con cần thay đổi dữ liệu đó, nó phải thông báo cho component cha, và component cha sẽ thay đổi State của nó (nếu dữ liệu đó là State của cha), sau đó truyền Props mới xuống.
- Dùng để cấu hình: Props giúp bạn làm cho các component trở nên linh hoạt và có thể tái sử dụng. Bạn có thể truyền các loại dữ liệu khác nhau qua Props: chuỗi (string), số (number), boolean, mảng (array), đối tượng (object), thậm chí là hàm (function) hoặc các element JSX khác.
Ví dụ về việc truyền Props:
Hãy xem một component đơn giản nhận Props:
function ChaoMung(props) {
// props là một đối tượng chứa tất cả các thuộc tính được truyền vào
return <p>Xin chào, {props.ten}!</p>;
}
// Sử dụng component ChaoMung trong component cha
function UngDung() {
return (
<div>
<ChaoMung ten="React" />
<ChaoMung ten="Developer" />
</div>
);
}
Trong ví dụ này, component UngDung
(cha) truyền thuộc tính ten
với giá trị là “React” và “Developer” xuống component ChaoMung
(con). Component ChaoMung
nhận giá trị này qua đối tượng props
và sử dụng props.ten
để hiển thị tên.
Bạn có thể truyền nhiều Props cùng lúc:
function BaiViet(props) {
return (
<div>
<h3>{props.tieuDe}</h3>
<p>Tác giả: {props.tacGia}</p>
<div>{props.noiDung}</div>
</div>
);
}
// Sử dụng
function DanhSachBaiViet() {
return (
<div>
<BaiViet tieuDe="Học Props" tacGia="Dev A" noiDung="Nội dung về Props..." />
<BaiViet tieuDe="Học State" tacGia="Dev B" noiDung="Nội dung về State..." />
</div>
);
}
Props giúp component BaiViet
có thể hiển thị nội dung khác nhau tùy thuộc vào dữ liệu được truyền từ component cha mà không cần thay đổi mã nguồn bên trong BaiViet
.
Ai kiểm soát Props? Component cha là người kiểm soát giá trị của Props. Component con chỉ đơn giản là nhận và sử dụng chúng.
State: Bộ Nhớ Nội Bộ Của Component
Nếu Props là dữ liệu được truyền từ bên ngoài vào, thì State là dữ liệu được quản lý bên trong một component. State đại diện cho trạng thái hiện tại của component – những dữ liệu có thể thay đổi theo thời gian do tương tác của người dùng hoặc các sự kiện khác.
Bản chất của State:
- Nội bộ: State thuộc về và được quản lý bởi chính component đó.
- Có thể thay đổi (Mutable): Không giống như Props, State được thiết kế để thay đổi. Khi State thay đổi, React sẽ tự động re-render (render lại) component đó và các component con của nó nhận dữ liệu State mới (nếu State đó được truyền xuống qua Props).
- Quản lý dữ liệu động: State dùng để lưu trữ những dữ liệu có thể thay đổi sau khi component được render lần đầu, ví dụ: giá trị nhập vào từ form, trạng thái hiển thị/ẩn của một phần tử, dữ liệu từ API sau khi fetch, số lượng sản phẩm trong giỏ hàng…
Trong Functional Components hiện đại, chúng ta sử dụng Hook useState
để làm việc với State.
Ví dụ về việc sử dụng State:
Hãy tạo một component đếm số đơn giản:
import React, { useState } from 'react';
function BoDem() {
// Khai báo State "soLuong" với giá trị ban đầu là 0
const [soLuong, setSoLuong] = useState(0);
// Hàm xử lý khi click nút
const tangSoLuong = () => {
// Cập nhật State. Khi State thay đổi, component sẽ re-render.
setSoLuong(soLuong + 1);
};
const giamSoLuong = () => {
setSoLuong(soLuong - 1);
};
return (
<div>
<p>Số lượng hiện tại: <strong>{soLuong}</strong></p>
<button onClick={tangSoLuong}>Tăng</button>
<button onClick={giamSoLuong}>Giảm</button>
</div>
);
}
Trong ví dụ này:
- Chúng ta sử dụng
useState(0)
để khai báo một State variable tên làsoLuong
và hàm để cập nhật nó làsetSoLuong
. Giá trị ban đầu củasoLuong
là 0. - Khi người dùng click vào nút “Tăng”, hàm
tangSoLuong
được gọi. - Bên trong
tangSoLuong
, chúng ta gọisetSoLuong(soLuong + 1)
để yêu cầu React cập nhật giá trị củasoLuong
. - React nhận thấy State đã thay đổi, nó sẽ re-render component
BoDem
. Lúc này,soLuong
sẽ có giá trị mới, và giao diện sẽ hiển thị giá trị đã được cập nhật.
Ai kiểm soát State? Chính component sở hữu State đó là người kiểm soát nó. State không thể được truy cập hoặc thay đổi trực tiếp bởi component cha hoặc các component ngang cấp.
Props và State: Những Khác Biệt Chính
Để tóm tắt lại, đây là bảng so sánh giữa Props và State:
Đặc điểm | Props | State |
---|---|---|
Nguồn gốc dữ liệu | Được truyền từ component cha xuống | Được quản lý bên trong component đó |
Tính biến đổi (Mutability) | Bất biến (Read-only) đối với component nhận | Có thể thay đổi bởi chính component đó |
Mục đích | Cấu hình component, truyền dữ liệu từ trên xuống | Quản lý dữ liệu thay đổi theo thời gian bên trong component |
Cách sử dụng | Nhận qua tham số của function component (đối tượng props ) |
Sử dụng Hook useState (trong functional components) |
Ai kiểm soát | Component cha (truyền Props) | Chính component sở hữu State |
Mối Quan Hệ Giữa Props và State: Dòng Chảy Dữ Liệu
Hiểu rõ sự khác biệt là quan trọng, nhưng hiểu cách Props và State làm việc cùng nhau còn quan trọng hơn. Dòng chảy dữ liệu trong React là một chiều (unidirectional data flow) và thường đi từ trên xuống:
- Một component cha có thể có State của riêng nó.
- Component cha đó có thể truyền dữ liệu từ State của nó xuống component con thông qua Props.
- Component con nhận dữ liệu này dưới dạng Props và sử dụng nó để render giao diện.
- Nếu State của component cha thay đổi, Props được truyền xuống component con cũng sẽ thay đổi, khiến component con được re-render với dữ liệu mới.
- Component con không được thay đổi Props nhận được. Nếu component con muốn “thông báo” cho component cha biết điều gì đó đã xảy ra (ví dụ: người dùng click nút), nó sẽ gọi một hàm được truyền xuống từ component cha qua Props. Hàm này thường sẽ là hàm cập nhật State trong component cha.
Ví dụ về Dòng Chảy Dữ Liệu Một Chiều:
Hãy tạo một component cha quản lý tên người dùng bằng State và truyền tên đó xuống component con để hiển thị.
import React, { useState } from 'react';
// Component con chỉ nhận và hiển thị tên qua Props
function HienThiTen(props) {
return (
<h3>Tên người dùng: <strong>{props.tenNguoiDung}</strong></h3>
);
}
// Component cha quản lý State và truyền xuống component con
function QuanLyNguoiDung() {
const [ten, setTen] = useState('Khách');
const [giaTriNhap, setGiaTriNhap] = useState(''); // State cho ô input
const xuLyThayDoiInput = (event) => {
setGiaTriNhap(event.target.value); // Cập nhật State của input
};
const capNhatTen = () => {
setTen(giaTriNhap); // Cập nhật State của tên người dùng
};
return (
<div>
{/* Truyền State 'ten' xuống component con HienThiTen qua Props */}
<HienThiTen tenNguoiDung={ten} />
<input
type="text"
value={giaTriNhap} // Giá trị của input được kiểm soát bởi State 'giaTriNhap'
onChange={xuLyThayDoiInput}
placeholder="Nhập tên mới"
/>
<button onClick={capNhatTen}>Cập nhật Tên</button>
</div>
);
}
Phân tích ví dụ:
- Component
QuanLyNguoiDung
(cha) có hai State:ten
(tên người dùng chính thức) vàgiaTriNhap
(giá trị tạm thời trong ô input). - Nó truyền State
ten
xuống component conHienThiTen
thông qua Props có têntenNguoiDung
. Component con chỉ đơn giản là hiển thị giá trị này. Nó không biết và không quan tâm đến nguồn gốc của dữ liệu (là State ở component cha). - Khi người dùng gõ vào ô input, sự kiện
onChange
xảy ra, gọi hàmxuLyThayDoiInput
. Hàm này cập nhật StategiaTriNhap
. VìgiaTriNhap
là State củaQuanLyNguoiDung
, component này re-render, nhưng giá trị hiển thị trongHienThiTen
vẫn là Stateten
cũ. - Khi người dùng click nút “Cập nhật Tên”, hàm
capNhatTen
được gọi. Hàm này lấy giá trị từ StategiaTriNhap
và dùng nó để cập nhật Stateten
. - Việc State
ten
thay đổi khiến componentQuanLyNguoiDung
re-render. Lần render này, nó truyền giá trị Stateten
mới xuống component conHienThiTen
qua PropstenNguoiDung
. - Component
HienThiTen
nhận Props mới và re-render, hiển thị tên người dùng đã được cập nhật.
Đây chính là cách dữ liệu di chuyển từ State của cha xuống con thông qua Props. Dòng chảy luôn là từ trên xuống.
Khi Nào Dùng Props, Khi Nào Dùng State?
Câu hỏi kinh điển! Dựa trên bản chất và mục đích của chúng, bạn có thể đưa ra quyết định dễ dàng:
- Sử dụng State khi: Dữ liệu đó thuộc về component đó và có khả năng thay đổi theo thời gian do tương tác của người dùng hoặc các sự kiện khác. State quản lý “trạng thái” động nội bộ của component.
- Sử dụng Props khi: Dữ liệu đó được truyền từ component cha xuống component con để cấu hình hoặc cung cấp thông tin cho component con. Props dùng để truyền dữ liệu từ bên ngoài vào component.
Hãy tự hỏi: “Dữ liệu này có cần thay đổi không? Nếu có, ai sở hữu và quản lý sự thay đổi đó?”
- Nếu component hiện tại sở hữu sự thay đổi đó -> Dùng State.
- Nếu dữ liệu đó đến từ component khác (thường là cha) và không cần thay đổi trong component hiện tại -> Dùng Props.
Lưu Ý Quan Trọng và Những Sai Lầm Thường Gặp
- Không thay đổi Props: Tuyệt đối không được cố gắng thay đổi giá trị của Props bên trong component nhận nó. Props là bất biến đối với component con. Nếu bạn cần thay đổi dựa trên Props, hãy lưu giá trị Props vào State nội bộ của component con (chỉ khi thực sự cần thiết và hiểu rõ lý do).
- Cập nhật State bất đồng bộ: Hàm cập nhật State (ví dụ:
setSoLuong
) hoạt động bất đồng bộ. Điều này có nghĩa là ngay sau khi gọisetSoLuong(soLuong + 1)
, giá trịsoLuong
*ngay lập tức* trong dòng code tiếp theo vẫn có thể là giá trị cũ. Nếu logic tiếp theo của bạn phụ thuộc vào giá trị State mới, bạn cần cẩn thận hoặc sử dụng form functional update củauseState
(ví dụ:setSoLuong(prevSoLuong => prevSoLuong + 1)
). - State của component cha không thể trực tiếp truy cập State của component con: Dữ liệu chỉ chảy một chiều từ cha xuống con. Để con “nói chuyện” với cha, cha cần truyền một hàm callback xuống con qua Props. Con sẽ gọi hàm callback này, truyền dữ liệu cần thiết lên, và hàm callback (được định nghĩa ở cha) sẽ xử lý, thường là cập nhật State của cha.
Kết Luận
Props và State là hai trụ cột trong việc quản lý dữ liệu trong các ứng dụng React. Nắm vững sự khác biệt và cách chúng tương tác là bước đi thiết yếu để xây dựng các giao diện người dùng phức tạp và có khả năng tương tác cao.
- Props: Giống như việc cấu hình một công cụ. Dữ liệu được truyền từ bên ngoài (cha) vào, là “read-only” đối với công cụ (con).
- State: Giống như bộ nhớ tạm thời hoặc trạng thái hoạt động nội bộ của công cụ. Dữ liệu được quản lý bên trong, có thể thay đổi và khiến công cụ phản ứng (re-render).
Bằng việc hiểu rõ Ai kiểm soát dữ liệu gì – component cha kiểm soát Props truyền xuống, và chính component đó kiểm soát State của nó – bạn sẽ có thể thiết kế cấu trúc component hợp lý và quản lý dòng chảy dữ liệu hiệu quả hơn rất nhiều.
Hy vọng bài viết này đã giúp bạn làm sáng tỏ sự khác biệt giữa Props và State. Đây là những khái niệm nền tảng sẽ theo bạn suốt chặng đường học React. Hãy thực hành thật nhiều để cảm nhận và làm chủ chúng nhé!
Trong bài viết tiếp theo của chuỗi React Roadmap, chúng ta sẽ khám phá sâu hơn về React Hook phổ biến nhất: useState
và cách sử dụng nó một cách hiệu quả. Đừng bỏ lỡ!