0
0
Lập trình
Thaycacac
Thaycacac thaycacac

🧱 Hướng Dẫn Xây Dựng Component React Hoàn Hảo

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

• 8 phút đọc

🧱 Hướng Dẫn Xây Dựng Component React Hoàn Hảo

Viết mã sạch không chỉ là làm cho mọi thứ hoạt động — mà còn là làm cho chúng dễ đọc, bảo trì và mở rộng.

React mang lại cho chúng ta sự linh hoạt tuyệt vời, nhưng nếu không có quy tắc, mã nguồn có thể nhanh chóng trở nên lộn xộn. Trong hướng dẫn này, chúng ta sẽ đi qua các thực tiễn tốt nhất trong ngành để cấu trúc các component React: từ việc đặt tên file đến tổ chức logic, xử lý props và viết JSX đẹp mắt.

Cuối cùng, bạn sẽ có một bản thiết kế để viết các component React đạt tiêu chuẩn chuyên nghiệp, có thể chịu đựng thử thách của thời gian, làm việc nhóm và khả năng mở rộng.


🔹 1. Đặt Tên và Cấu Trúc

Tính nhất quán là nền tảng của một mã nguồn sạch.

  • Đặt Tên File → Sử dụng kebab-case
    • user-profile.tsx
    • Điều này giúp tránh các vấn đề trên hệ thống nhạy cảm với chữ hoa như Unix.
  • Đặt Tên Component → Sử dụng PascalCase
    • function UserProfile() { ... }
  • Khai Báo Hàm so với Hàm Mũi Tên →
    • Ưu tiên khai báo hàm cho các component. Chúng giúp công cụ phát triển và linter suy ra tên component tự động, giúp việc gỡ lỗi dễ dàng hơn.
  • Xuất → Luôn xuất dưới dạng default trừ khi có lý do mạnh mẽ không làm vậy.

🔹 2. Props và Logic

Props định nghĩa hợp đồng của một component. Xử lý chúng một cách cẩn thận.

  • Sử Dụng Interfaces Thay Vì Types
    • Interfaces có thể mở rộng và cung cấp lỗi TypeScript rõ ràng hơn.
  • Destructuring vs. props.propName
    • ✅ Sử dụng destructuring nếu bạn có 1–3 props.
    • ✅ Sử dụng props.propName nếu bạn có nhiều props (giữ cho chữ ký hàm sạch).
  • Thứ Tự Logic Trong Một Component
    • Giữ cho component của bạn có thể dự đoán bằng cách làm theo thứ tự này:
      1. Custom hooks
      2. useState
      3. useRef
      4. Hàm trợ giúp
      5. Xử lý sự kiện
      6. useEffect
      7. Trả về sớm
      8. Trả về JSX
  • Hàm Trợ Giúp
    • Nếu chỉ sử dụng trong một component → giữ nó bên trong.
    • Nếu có thể tái sử dụng → di chuyển nó vào file utils/.
  • Trả Về Sớm
    • Xử lý trạng thái đang tải hoặc lỗi trước để tránh JSX lồng sâu.

🔹 3. Mã Sạch và Đọc Được

Mã đọc được = mã dễ bảo trì.

  • Biến Render
    • Định nghĩa các biến như buttonText trước JSX của bạn. Đừng làm rối JSX với logic inline.
  • HTML Ngữ Nghĩa
    • Sử dụng các thẻ có ý nghĩa (<section>, <header>, <footer>) thay vì vô hạn <div>.
  • Tính Nhất Quán Trong Styling
    • Chọn một hệ thống styling (ví dụ: Tailwind, CSS Modules) và giữ nó. Kết hợp nhiều hệ thống làm cho việc bảo trì khó khăn hơn.
  • Tái Cấu Trúc Khi Cần
    • Nếu một component trở nên quá lớn → chia nó thành các component nhỏ hơn hoặc custom hooks.

🔹 4. Ví Dụ: Component UserProfile Hoàn Hảo

Dưới đây là một component áp dụng tất cả các nguyên tắc này:

typescript Copy
// src/components/user-profile.tsx
import React, { useState, useEffect, useRef } from 'react';
import { useAuth } from '../hooks/useAuth';
import { fetchUserData } from '../utils/api';

interface UserProfileProps {
  userId: string;
  isEditor?: boolean;
}

export default function UserProfile({ userId, isEditor }: UserProfileProps) {
  /**
   * 1. Custom Hooks
   */
  const { isAuthorized } = useAuth();

  /**
   * 2. useState
   */
  const [user, setUser] = useState<any>(null);
  const [isLoading, setIsLoading] = useState(true);

  /**
   * 3. useRef
   * Ví dụ: theo dõi số lần tải profile
   */
  const loadCountRef = useRef<number>(0);

  /**
   * 4. Hàm Trợ Giúp
   */
  const formatLocation = (location: string) => {
    return location ? location.toUpperCase() : 'Không biết';
  };

  /**
   * 5. Xử Lý Sự Kiện
   */
  const handleEditClick = () => {
    alert('Chỉnh sửa profile...');
  };

  /**
   * 6. useEffect
   */
  useEffect(() => {
    const getData = async () => {
      const userData = await fetchUserData(userId);
      setUser(userData);
      setIsLoading(false);
      // tăng số lần tải
      loadCountRef.current += 1;
    };
    getData();
  }, [userId]);

  /**
   * 7. Trả Về Sớm
   */
  if (isLoading) return <div>Đang tải profile người dùng...</div>;
  if (!user) return <div>Không tìm thấy người dùng.</div>;

  /**
   * Biến Logic Render
   */
  const buttonText = isAuthorized ? 'Chỉnh sửa Profile' : 'Xem Profile';

  /**
   * 8. Trả Về JSX
   */
  return (
    <section className="user-profile border rounded p-4 shadow-sm">
      <header className="flex items-center justify-between mb-4">
        <h1 className="text-xl font-bold">{user.name}</h1>
        {isEditor && (
          <button
            onClick={handleEditClick}
            className="bg-blue-500 text-white px-3 py-1 rounded"
          >
            {buttonText}
          </button>
        )}
      </header>
      <div className="profile-details space-y-2">
        <p>Email: {user.email}</p>
        <p>Địa chỉ: {formatLocation(user.location)}</p>
        <p className="text-sm text-gray-500">
          Profile đã được tải {loadCountRef.current} lần
        </p>
      </div>
    </section>
  );
}

🔹 5. React Hiện Đại với Các Tính Năng Đồng Thời

React 18+ đã giới thiệu các tính năng đồng thời giúp giao diện người dùng trở nên mượt mà và tương tác hơn.

  • Suspense → cách xử lý tải bất đồng bộ một cách khai báo
  • useTransition → giữ cho giao diện người dùng phản hồi trong quá trình cập nhật trạng thái
  • useDeferredValue → ngăn chặn độ trễ khi gõ trong các input tìm kiếm

Những hook này là những công cụ thay đổi cuộc chơi trong việc xây dựng các ứng dụng hiện đại và phản hồi nhanh.


🔹 6. Ví Dụ: Component SearchUsers (React Hiện Đại)

typescript Copy
// src/components/search-users.tsx
"use client";

import React, {
  useState,
  useTransition,
  Suspense,
  useDeferredValue,
  useEffect,
} from "react";
import { fetchUsers } from "../utils/api";

interface User {
  id: string;
  name: string;
  email: string;
}

/**
 * Component Con: Hiển thị danh sách người dùng
 */
function UserList({ query }: { query: string }) {

  const deferredQuery = useDeferredValue(query);
  const [users, setUsers] = useState<User[]>([]);
  const [isPending, startTransition] = useTransition();

  useEffect(() => {
    startTransition(async () => {
      const data = await fetchUsers(deferredQuery);
      setUsers(data);
    });
  }, [deferredQuery]);

  if (isPending) return <p>Đang tải người dùng...</p>;

  if (!users.length) return <p>Không tìm thấy người dùng.</p>;

  return (
    <ul className="space-y-2">
      {users.map((u) => (
        <li key={u.id} className="p-2 border rounded">
          <strong>{u.name}</strong> - {u.email}
        </li>
      ))}
    </ul>
  );
}

/**
 * Component Cha: Hộp tìm kiếm + Suspense
 */
export default function SearchUsers() {
  const [query, setQuery] = useState("");

  return (
    <section className="search-users border rounded p-4 shadow-sm">
      <header className="mb-4">
        <h2 className="text-xl font-bold">Tìm Kiếm Người Dùng</h2>
        <input
          type="text"
          placeholder="Nhập tên..."
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          className="p-2 border rounded w-full"
        />
      </header>
      <Suspense fallback={<p>Đang tải kết quả tìm kiếm...</p>}>
        <UserList query={query} />
      </Suspense>
    </section>
  );
}

🔹 7. Tại Sao Điều Này Quan Trọng

  • Suspense → giữ cho logic tải sạch và khai báo
  • useTransition → ngăn chặn độ trễ đầu vào trong khi lấy dữ liệu
  • useDeferredValue → trì hoãn cập nhật để tránh tái render tốn kém trong mỗi lần gõ

💡 Mẹo chuyên nghiệp: Hãy thử thêm useOptimistic (React 19) để có phản hồi giao diện tức thì khi thay đổi dữ liệu. Nó giúp các hành động của người dùng cảm thấy như thời gian thực.

Những tính năng này giúp các ứng dụng cảm thấy nhanh, mượt mà và hiện đại.


✅ Những Điểm Chính Cần Nhớ

  • Tính nhất quán là điều quan trọng nhất. Sử dụng cùng một quy tắc ở mọi nơi.
  • Mã đọc được tiết kiệm thời gian. Trả về sớm, HTML ngữ nghĩa và biến render giữ cho mọi thứ rõ ràng.
  • Tái cấu trúc để tăng tính mô-đun. Chia nhỏ các component phức tạp thành các component nhỏ hơn, có thể tái sử dụng.
  • Chấp nhận React hiện đại. Sử dụng Suspense, transitions và deferred values để có trải nghiệm người dùng tốt nhất.

💡 Mẹo chuyên nghiệp: Hãy lấy một trong những component cũ lộn xộn của bạn và tái cấu trúc nó theo các quy tắc này. Bạn sẽ ngay lập tức thấy sự khác biệt về độ rõ ràng, khả năng bảo trì và hiệu suất.

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