Như đã tìm hiểu trong phần lõi của ReactJS trong bài trước, ReactJS dựa vào sự thay đổi của trạng thái để quyết định việc render lại html, vậy nên việc quản lý trạng thái là cực kỳ quan trọng. Trong bài này Dũng sẽ cùng các bạn tìm hiểu về quản lý trạng thái trong ReactJS nhé.

Khởi tạo dự án

Đầu tiên chúng ta sẽ khởi tạo dự án state-management bằng cách sử dụng câu lệnh:

yarn create vite state-management

Bạn đừng quên lựa chọn framework là ReactJS và ngôn ngữ là Typescript nhé.
Sau khi bạn khởi tạo dự án xong hãy cd vào thư mục state-management vừa được tạo và gõ lệnh yarn để tải các thư viện cần thiết và chạy lệnhyarn dev
để khởi chạy server và bạn có thể truy cập vào trang web thông qua địa chỉ: http://localhost:5173/

Sử dụng react hook

Trạng thái kiểu giá trị

Mặc định thì vite sẽ khởi tạo cho chúng ta sử dụng react hook cho việc quản lý trạng thái, cụ thể thị bạn có thể thấy khai báo:

const [count, setCount] = useState(0)

Ở đây chúng ta khai báo biến trạng thái count và một hàm setCount để cập nhật trạng thái cho biên count. Mỗi khi chúng ta nhấn nút count is nó tăng trạng thái count lên 1 đơn vị và reactjs sẽ hiển thị giá trị được tăng lên.
Có một điều đặc biệt với đoạn mã cập nhật biến count lên 1 đơn vị:

<button onClick={() => setCount((count) => count + 1)}>

Tại sao chúng ta không sử dụng kiểu như dưới đây?

<button onClick={() => setCount(count + 1)}>

Để hiểu rõ được vấn đề này chúng ta cần phải nhìn sâu vào bên trong hàm cập nhật trạng thái của react js, nó nằm ở tập tin ReactBaseClasses.js:

Component.prototype.setState = function (partialState, callback) {
  if (
    typeof partialState !== 'object' &&
    typeof partialState !== 'function' &&
    partialState != null
  ) {
    throw new Error(
      'takes an object of state variables to update or a ' +
        'function which returns an object of state variables.',
    );
  }

  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

Đoạn mã này nói cho chúng ta biết rằng khi chúng ta cập nhật trạng thái của một biến thì giá trị của biến đó chưa thực sự được cập nhật ngay mà nó chỉ được đưa vào trong queue để chờ đến chu kỳ của scheduler thì mới được cập nhật, chính vì điều này mà khi chúng ta sử dụng setCount(count + 1) giả sử người dùng họ nhấn liên tiếp 2 lần nghĩa là sinh ra 2 lần gọi:

setCount(count + 1)
setCount(count + 1)

Nó sẽ không thể đảm bảo được biến count sẽ tăng lên 2 lần nếu chu kỳ cập nhật trạng thái chưa đến và biến count có thể vẫn giữa nguyên giá trị và chỉ được tăng lên 1 lần.
Còn kiểu setCount((count) => count + 1)là kiểu sử dụng labda function, sẽ có 2 lần gọi hàm tương ứng với 2 lần gọi setCount và biến count có thể được tăng lên 2 đơn vị.

Trạng thái kiểu đối tượng

Hãy nói chúng ta có một giao diện kiểu này:

interface Data {
  count: number;
}

Và chúng ta sẽ bổ sung thêm một biến trạng thái như sau:

const [data, setData] = useState<Data>({count: 0});

Nếu chúng ta sử dụng việc cập nhật trạng thái như sau:

<button onClick={() => ++data.count}>
  data is data ({data.count})
</button>

Nghĩa là bạn chỉ thay đổi giá trị của biến count trong đối tượng data có sẵn thị React sẽ không biết được có trạng thái thay đổi để mà render lại bạn có thể kiểm chứng bằng cách nhấn vào nút data is data. Vậy nên bạn sẽ cần thay đổi mã nguồn thành:

import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

interface Data {
  count: number;
}

function App() {
  const [count, setCount] = useState(0)
  const [data, setData] = useState<Data>({count: 0});

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <button onClick={() => setData({count: ++data.count})}>
          data is data ({data.count})
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  )
}

export default App

Lúc này khi bạn nhấn nút data is data bạn sẽ thấy giá trị tăng lên sau mỗi lần nhấn nút.

Sử dụng kiểu truyền thống

Chúng ta sẽ thay đổi mã nguồn của tập tin App.tsx như sau:

import React from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

interface Data {
  count: number;
}

interface State {
  count: number;
  data: Data;
}

class App extends React.Component {

  constructor(props: any) {
    super(props)
    this.state = {
      count: 0,
      data: {count: 0}
    }
  }

  setCount(count: number) {
    this.setState({
      count: count
    });
  }

  setData(data: Data) {
    this.setState({
      data: data
    });
  }

  public render() {
    const {count, data} = this.state as State;
    return (
      <>
        <div>
          <a href="https://vitejs.dev" target="_blank">
            <img src={viteLogo} className="logo" alt="Vite logo" />
          </a>
          <a href="https://react.dev" target="_blank">
            <img src={reactLogo} className="logo react" alt="React logo" />
          </a>
        </div>
        <h1>Vite + React</h1>
        <div className="card">
          <button onClick={() => this.setCount(count + 1)}>
            count is {count}
          </button>
          <button onClick={() => this.setData({count: data.count + 1})}>
            data is data ({data.count})
          </button>
          <p>
            Edit <code>src/App.tsx</code> and save to test HMR
          </p>
        </div>
        <p className="read-the-docs">
          Click on the Vite and React logos to learn more
        </p>
      </>
    )
  }
}

export default App

Ở đây chúng ta sẽ sử dụng hàm setState truyền thống, mặc dù trong có vẻ dễ hiểu hơn nhưng code lại bị dài hơn, vậy nên bạn hãy cân nhắc sử dụng cú pháp nào mà bạn thích nhé.

Tổng kết

Như vậy chúng ta đã cùng nhau tìm hiểu một chút về quản lý trạng thái trong ReactJS.


Cám ơn bạn đã quan tâm đến bài viết này. Để nhận được thêm các kiến thức bổ ích bạn có thể:

  1. Đọc các bài viết của TechMaster trên facebook: https://www.facebook.com/techmastervn
  2. Xem các video của TechMaster qua Youtube: https://www.youtube.com/@TechMasterVietnam nếu bạn thấy video/bài viết hay bạn có thể theo dõi kênh của TechMaster để nhận được thông báo về các video mới nhất nhé.
  3. Chat với techmaster qua Discord: https://discord.gg/yQjRTFXb7a