0
0
Lập trình
Harry Tran
Harry Tran106580903228332612117

Quản lý trạng thái thông minh trong React với TypeScript

Đăng vào 8 tháng trước

• 5 phút đọc

Quản lý trạng thái thông minh trong React với TypeScript

Quản lý trạng thái trong React trở nên dễ dự đoán và an toàn hơn khi bạn kết hợp sức mạnh của hệ thống kiểu của TypeScript với các tính năng không thay đổi như readonly. Trong bài viết này, chúng ta sẽ khám phá cách định kiểu mạnh cho trạng thái của bạn bằng useState(), đảm bảo tính bất biến bằng cách sử dụng readonly, và thậm chí đi sâu hơn với ReadonlyDeep cho các cấu trúc phức tạp. Những mẫu này không chỉ cải thiện IntelliSense và ngăn ngừa lỗi mà còn phù hợp hoàn hảo với triết lý render của React.

🧠 useState<Type>() trong TypeScript

  • Định kiểu mạnh cho các biến trạng thái giúp an toàn hơn và cải thiện IntelliSense.
  • Ngăn chặn sự không tương thích kiểu trong quá trình cập nhật trạng thái.
  • Khuyến khích quản lý trạng thái dễ dự đoán và bảo trì hơn.
typescript Copy
const [count, setCount] = useState<number>(0); // Được định kiểu mạnh là number

🔒 readonly trong TypeScript

  • Đảm bảo tính bất biến cho các đối tượng và mảng.
  • Ngăn chặn việc gán lại hoặc thay đổi thuộc tính.
  • Hữu ích để bảo vệ cấu trúc trạng thái khỏi những thay đổi không mong muốn.

Từng thuộc tính readonly (thủ công)

typescript Copy
type User = {
  readonly id: number;
  readonly name: string;
};

const [user, setUser] = useState<User>({
  id: 1,
  name: 'Arka',
});

// user.id = 2; ❌ Lỗi: Không thể gán giá trị cho 'id' vì nó là thuộc tính chỉ đọc

Sử dụng Readonly trực tiếp (tự động)

typescript Copy
type User = {
  id: number;
  name: string;
};

const [user, setUser] = useState<Readonly<User>>({
  id: 1,
  name: 'Arka',
});

// user.id = 2; ❌ Lỗi: Không thể gán giá trị cho 'id' vì nó là thuộc tính chỉ đọc

// Cập nhật trạng thái một cách an toàn
const updateName = (newName: string) => {
  setUser({ ...user, name: newName }); // ✅ Cập nhật không thay đổi
};

📦 readonly so với ReadonlyDeep

  • readonly chỉ áp dụng cho các thuộc tính cấp cao nhất.
  • Để có tính bất biến sâu, hãy sử dụng các loại tiện ích như ReadonlyDeep<T> (thông qua thư viện hoặc các loại tùy chỉnh).
  • Trạng thái React thường hưởng lợi từ tính bất biến nông, nhưng tính bất biến sâu có thể giúp trong các cấu trúc phức tạp.

Cài đặt type-fest

bash Copy
npm install type-fest

Ví dụ React + TypeScript với ReadonlyDeep

typescript Copy
import { useState } from 'react';
import type { ReadonlyDeep } from 'type-fest';

type Address = {
  city: string;
  zip: number;
};

type User = {
  id: number;
  name: string;
  address: Address;
};

function Profile() {
  const [user, setUser] = useState<ReadonlyDeep<User>>({
    id: 1,
    name: 'Arka',
    address: {
      city: 'Bangalore',
      zip: 560001,
    },
  });

  // ❌ Tất cả sẽ gây lỗi do readonly sâu
  // user.name = 'New Name';
  // user.address.city = 'Mumbai';

  const updateCity = (newCity: string) => {
    setUser({
      ...user,
      address: {
        ...user.address,
        city: newCity,
      },
    });
  };

  return (
    <div>
      <p>{user.name} sống ở {user.address.city}</p>
      <button onClick={() => updateCity('Mumbai')}>Chuyển đến Mumbai</button>
    </div>
  );
}

⚠️ Tại sao readonly hữu ích cho đối tượng và mảng

  • Làm cho toàn bộ đối tượng hoặc mảng không thay đổi, không chỉ các khóa đơn lẻ.
  • Ngăn chặn sự thay đổi trực tiếp, điều này có thể phá vỡ chu kỳ render của React.
  • Đặc biệt quan trọng khi truyền props hoặc quản lý trạng thái chia sẻ.
typescript Copy
const numbers: readonly number[] = [1, 2, 3];

// numbers.push(4); ❌ Lỗi: push không tồn tại trên mảng readonly

🚫 Tránh thay đổi trực tiếp trạng thái

  • Luôn sử dụng hàm setter từ useState.
  • Thay đổi trực tiếp có thể dẫn đến render lỗi thời hoặc hành vi không mong muốn.
  • readonly giúp thực thi mẫu này và phát hiện lỗi sớm.
typescript Copy
import { useState } from 'react';

function TodoList() {
  const [todos, setTodos] = useState<string[]>(['Học TS']);

  const addTodo = (todo: string) => {
    // ❌ Thay đổi trực tiếp (không tốt)
    // todos.push(todo);

    // ✅ Cách đúng
    setTodos([...todos, todo]);
  };

  return <button onClick={() => addTodo('Viết blog')}>Thêm Todo</button>;
}

🧩 Mảng readonly trong React

  • Ngăn chặn việc sử dụng các phương thức thay đổi như push, pop, splice, v.v.
  • Khuyến khích sử dụng các mẫu không thay đổi như map, filter, concat.
  • Phù hợp với kỳ vọng của React về tính bất biến cho việc render lại.

✅ Thực hành tốt nhất

  • Kết hợp useState với kiểu định nghĩa phù hợp để cập nhật an toàn hơn.
  • Sử dụng readonly trong các loại để bảo vệ cấu trúc dữ liệu.
  • Tránh thay đổi để duy trì hành vi render dễ dự đoán của React.

⚠️ Các cạm bẫy thường gặp

  • Không hiểu rõ về tính bất biến có thể dẫn đến lỗi không mong muốn.
  • Thường xuyên kiểm tra các thuộc tính readonly để tránh thay đổi sai.

⚡ Mẹo hiệu suất

  • Sử dụng cấu trúc dữ liệu không thay đổi giúp giảm thiểu render không cần thiết.
  • Theo dõi trạng thái cẩn thận để tối ưu hóa hiệu suất ứng dụng.

❓ Câu hỏi thường gặp

1. Tại sao nên sử dụng readonly trong TypeScript?
readonly giúp bảo vệ dữ liệu khỏi thay đổi không mong muốn, làm cho mã nguồn an toàn hơn và dễ bảo trì.

2. Làm thế nào để cập nhật trạng thái một cách an toàn trong React?
Luôn sử dụng hàm setter từ useState và tránh thay đổi trực tiếp trạng thái.

3. Có những công cụ nào hỗ trợ readonly trong TypeScript?
Thư viện type-fest cung cấp nhiều loại tiện ích hỗ trợ tính bất biến sâu và các loại tương tự.

Kết thúc bài viết, việc kết hợp TypeScript với React sẽ giúp bạn quản lý trạng thái một cách thông minh hơn, giảm thiểu lỗi và cải thiện khả năng bảo trì mã nguồn. Nếu bạn thấy bài viết này hữu ích, hãy chia sẻ với cộng đồng phát triển của bạn!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào