State trong ReactJS: Khái niệm, cách sử dụng và ví dụ chi tiết

VnnTools

Khi bước chân vào thế giới React, bên cạnh props, bạn sẽ ngay lập tức nghe thấy một khái niệm cốt lõi, một trụ cột không thể thiếu: state. Nếu ví một component React như một cơ thể sống, thì props giống như DNA được truyền từ cha mẹ, còn state chính là ký ức, là trạng thái cảm xúc, là những gì thay đổi bên trong cơ thể đó theo thời gian và tương tác.

State trong ReactJS: Khái niệm, cách sử dụng và ví dụ chi tiết

Hiểu rõ state không chỉ là biết một phần của React, mà là nắm được chìa khóa để xây dựng các ứng dụng web động, có tính tương tác cao và mang lại trải nghiệm người dùng tuyệt vời.

Bài viết này sẽ là tài liệu toàn diện giúp bạn giải mã tất cả về state: từ khái niệm cơ bản nhất, cách sử dụng qua hook useState, những quy tắc vàng cần tuân thủ, cho đến việc phân biệt với props và quản lý các state phức tạp.

1. State là gì? Một cách giải thích dễ hiểu nhất

Hãy tưởng tượng một chiếc công tắc đèn. Nó có hai trạng thái: BẬTTẮT. Giao diện người dùng (UI) của bạn sẽ hiển thị hình ảnh bóng đèn sáng khi trạng thái là "BẬT" và hình ảnh bóng đèn tối khi trạng thái là "TẮT".

Trong React, State là một đối tượng JavaScript dùng để lưu trữ dữ liệu nội bộ của một component. Dữ liệu này có thể thay đổi theo thời gian, thường là do tương tác của người dùng.

Điểm kỳ diệu và quan trọng nhất cần nhớ là: Khi state của một component thay đổi, React sẽ tự động render lại (re-render) component đó để cập nhật giao diện người dùng (UI) theo dữ liệu mới.

Đây chính là cơ chế tạo nên tính "phản ứng" (Reactive) của React. Bạn không cần phải trực tiếp thay đổi HTML bằng tay. Bạn chỉ cần thay đổi state, và React sẽ lo phần còn lại.

2. Tại sao chúng ta cần State?

Nếu không có state, ứng dụng của bạn sẽ chỉ là một trang web tĩnh, giống như một tờ báo in. Mọi thứ đều cố định. State mang lại sự sống cho ứng dụng bằng cách cho phép nó:

  • Phản hồi lại tương tác của người dùng: Bấm một nút, điền vào một form, bật/tắt một chế độ...
  • Hiển thị dữ liệu thay đổi theo thời gian: Một chiếc đồng hồ đếm ngược, thông báo mới, dữ liệu được tải về từ máy chủ (API).
  • Kiểm soát luồng hoạt động của component: Một modal có thể được hiển thị hoặc ẩn đi, một menu có thể được mở hoặc đóng.

3. "Người bạn đồng hành" không thể thiếu: useState

Trong React hiện đại với Functional Components, chúng ta quản lý state bằng một hook đặc biệt tên là useState. Hook này là cách đơn giản và phổ biến nhất để thêm state vào component của bạn.

Cú pháp của useState

import { useState } from 'react'

function MyComponent() {
  const [state, setState] = useState(initialValue)
  // ...
}

Hãy cùng "mổ xẻ" cú pháp này:

  • useState(): Là một hàm bạn gọi bên trong functional component để tạo ra một "mẩu" state.
  • initialValue: Là giá trị khởi tạo của state. Nó có thể là bất cứ thứ gì: một con số, một chuỗi, một boolean (true/false), một object, một array... Giá trị này chỉ được dùng trong lần render đầu tiên.
  • state: Là biến chứa giá trị hiện tại của state. Trong ví dụ trên, nó sẽ có giá trị là initialValue ở lần render đầu tiên.
  • setState: Là hàm bạn dùng để cập nhật giá trị của state. Khi bạn gọi hàm này với một giá trị mới, React sẽ lên lịch render lại component.

Ví dụ kinh điển: Bộ đếm (Counter)

Đây là ví dụ kinh điển của state trong React.

import React, { useState } from 'react'

function Counter() {
  // 1. Khai báo state 'count' với giá trị ban đầu là 0
  const [count, setCount] = useState(0)

  // Hàm xử lý khi nhấn nút tăng
  const handleIncrement = () => {
    // 2. Dùng hàm setCount để cập nhật state
    setCount(count + 1)
  }

  // Hàm xử lý khi nhấn nút giảm
  const handleDecrement = () => {
    // 3. Cũng dùng hàm setCount để cập nhật state
    setCount(count - 1)
  }

  return (
    <div>
      <h1>Bộ đếm</h1>
      {/* 3. Hiển thị giá trị hiện tại của state */}
      <p>Số lượt bấm hiện tại: {count}</p>
      <button onClick={handleIncrement}>Tăng (+)</button>
      <button onClick={handleDecrement}>Giảm (-)</button>
    </div>
  )
}

export default Counter

Trong ví dụ này:

  1. Ta khai báo một state tên là count với giá trị ban đầu là 0.
  2. Khi người dùng nhấn nút "Tăng", hàm handleIncrement được gọi. Bên trong, hàm setCount(count + 1) được thực thi.
  3. React nhận thấy yêu cầu thay đổi state. Nó sẽ render lại component Counter.
  4. Trong lần render lại này, biến count giờ sẽ có giá trị mới (ví dụ là 1), và giao diện sẽ được cập nhật để hiển thị <p>Số lượt bấm hiện tại: 1</p>.

4. Những "nguyên tắc vàng" khi làm việc với State

Để làm việc hiệu quả và tránh các lỗi khó tìm, hãy luôn ghi nhớ các quy tắc sau:

Không bao giờ thay đổi State trực tiếp

Đây là lỗi phổ biến nhất của người mới bắt đầu.

// ❌ SAI!
// Đừng bao giờ làm thế này. React sẽ không biết để render lại.
this.state.comment = 'Hello' // Đối với Class Component
myState.name = 'John' // Đối với Functional Component

// ✅ ĐÚNG!
// Luôn dùng hàm setter (setState) được cung cấp.
this.setState({ comment: 'Hello' }) // Đối với Class Component
setMyState({ ...myState, name: 'John' }) // Đối với Functional Component

Tại sao? React chỉ kích hoạt việc render lại khi hàm setState được gọi. Việc thay đổi trực tiếp sẽ không thông báo cho React biết, dẫn đến dữ liệu thay đổi nhưng giao diện không được cập nhật.

Cập nhật State có thể là bất đồng bộ

React có thể gộp nhiều lệnh setState lại với nhau thành một lần cập nhật duy nhất để tối ưu hiệu năng. Điều này có nghĩa là bạn không thể tin tưởng vào giá trị của state ngay sau khi vừa gọi setState.

function Counter() {
  const [count, setCount] = useState(0)

  const handleTripleClick = () => {
    // Nếu count đang là 0, bạn nghĩ count sẽ là 3?
    setCount(count + 1) // count vẫn là 0 ở đây
    setCount(count + 1) // count vẫn là 0 ở đây
    setCount(count + 1) // count vẫn là 0 ở đây
    // Kết quả cuối cùng: count sẽ chỉ tăng lên 1!
  }
}

Giải pháp: Khi giá trị state mới phụ thuộc vào giá trị state cũ, hãy truyền một hàm vào bên trong hàm setState. Hàm này sẽ nhận vào giá trị state trước đó prevState và trả về giá trị mới.

// ✅ ĐÚNG!
const handleTripleClick = () => {
  setCount((prevCount) => prevCount + 1)
  setCount((prevCount) => prevCount + 1)
  setCount((prevCount) => prevCount + 1)
  // Kết quả: count sẽ tăng lên 3 chính xác!
}

State là cục bộ và riêng tư

State của một component là hoàn toàn riêng tư. Không một component nào khác có thể truy cập trực tiếp vào state của nó, kể cả component cha hay con. Cách duy nhất để chia sẻ thông tin là truyền nó xuống dưới dạng props (từ cha xuống con).

5. State vs. Props trong React

Đây là điểm gây nhầm lẫn cho nhiều người. Hãy làm rõ nó bằng một bảng so sánh:

Tiêu chíStateProps
Nguồn gốcĐược quản lý bên trong component.Được truyền từ bên ngoài vào component (bởi component cha).
Khả năng thay đổiCó thể thay đổi bởi chính component đó (thông qua setState).Không thể thay đổi (read-only/immutable). Giống như tham số của hàm.
Mục đíchĐể lưu trữ dữ liệu thay đổi theo thời gian, tạo ra sự tương tác.Để cấu hình và truyền dữ liệu cho component con.
Luồng dữ liệuCục bộ, chỉ tồn tại trong component."Top-down" - từ cha xuống con.

Một câu "thần chú" dễ nhớ: Props được truyền vào component (như tham số của hàm), trong khi state được quản lý bên trong component (như biến được khai báo trong hàm).

6. Mở rộng: Quản lý State phức tạp

Khi ứng dụng của bạn lớn dần, useState đôi khi là chưa đủ. React và hệ sinh thái của nó cung cấp các công cụ mạnh mẽ hơn:

  • Reducer: Một hook khác của React là useReducer, phù hợp để quản lý các state object phức tạp với nhiều hành động cập nhật khác nhau (tương tự như Redux).
  • Context API: Giải pháp của React để chia sẻ state xuyên suốt cây component mà không cần phải "khoan" props (prop drilling) qua nhiều cấp.
  • Thư viện quản lý State: Đối với các ứng dụng cực lớn, các thư viện như Redux Toolkit, Zustand, hoặc Jotai cung cấp các giải pháp mạnh mẽ và có cấu trúc để quản lý state toàn cục (global state).

Nếu bạn đã đọc đến đây, xin chúc mừng! Bạn không chỉ biết "state là gì" mà còn hiểu được "tại sao", "như thế nào" và "khi nào" nên sử dụng nó.

Hãy nhớ rằng:

  • State là bộ nhớ của component.
  • useState là công cụ chính để tạo và cập nhật state.
  • Luôn tôn trọng các quy tắc về tính bất biến và cập nhật bất đồng bộ.
  • Hiểu rõ sự khác biệt giữa stateprops là nền tảng vững chắc.

Nắm vững state chính là bạn đã nắm vững được cách "thổi hồn" vào các ứng dụng React, biến chúng từ những trang tĩnh vô tri thành những trải nghiệm sống động và đầy tương tác.

Chúc bạn thành công trên hành trình chinh phục React!

Bài viết liên quan

Tất tần tật về Redux trong React: Định nghĩa, Nguyên lý và Ví dụ chi tiết

Khám phá sức mạnh của Redux trong React. Tìm hiểu nguyên lý hoạt động, các thành phần cốt lõi và ví dụ thực tế giúp bạn làm chủ State Management.

JSX trong React là gì? Hướng dẫn chi tiết cho người mới bắt đầu

JSX là cú pháp mở rộng của JavaScript, cho phép viết code giống HTML trong React. Bài viết này sẽ giúp bạn hiểu rõ JSX là gì, tại sao cần dùng JSX và cách sử dụng hiệu quả.

Two-way Binding trong React: Cách hoạt động & Ví dụ minh họa dễ hiểu

Hướng dẫn chi tiết về two-way binding trong React. Bài viết phân tích cách nó hoạt động, giải thích sự khác biệt so với các framework khác và cung cấp ví dụ thực tế.

Component React là gì? Hướng dẫn chi tiết cho người mới bắt đầu

Hiểu rõ Component React chính là chìa khóa để xây dựng giao diện ứng dụng web hiện đại. Đọc ngay để nắm vững các loại component phổ biến và cách tạo component đầu tiên của bạn.