Mục lục

Clean Code: Giải phẫu một Component hoàn hảo

Quy tắc 7 bước để viết một file React Component chuẩn mực. Cách đặt tên props, thứ tự import, tách logic (Custom Hook), và xử lý magic numbers để code dễ đọc như văn xuôi.

Một file code đẹp giống như một bài văn hay: Có mở bài, thân bài, kết bài, và bố cục rõ ràng.

1. The Anatomy (Cấu trúc giải phẫu)

Đừng viết lộn xộn. Hãy tuân thủ thứ tự này TRÊN MỌI FILE:

  1. Imports: (Nhóm theo thứ tự: React -> Library -> Internal -> Styles).
  2. Types/Interfaces: Định nghĩa props ngay đầu file.
  3. Constants: Các biến tĩnh không đổi (MAX_ITEMS = 5).
  4. Component Function:
    • Destructuring Props.
    • Hooks (useState, useEffect).
    • Custom Hooks (Logic nghiệp vụ).
    • Helper Functions (Hàm xử lý sự kiện).
    • Early Returns (Loading, Error states).
    • Render (JSX).
  5. Sub-components (Nếu nhỏ và chỉ dùng trong file này).

2. Quy tắc Đặt tên (Naming Convention)

Tên biến là thứ khó nhất trong lập trình. Hãy tuân thủ chuẩn:

Event Handlers vs Props

  • Props truyền xuống: Bắt đầu bằng on. (onClick, onUserSelect). -> Ý nghĩa: "Khi sự kiện này xảy ra".
  • Hàm xử lý bên trong: Bắt đầu bằng handle. (handleClick, handleUserSelect). -> Ý nghĩa: "Xử lý việc đó".
tsx:
// ✅ Chuẩn
<Button onClick={handleSave} />

Boolean (Flags)

Luôn bắt đầu bằng is, has, should.

  • open, disable, valid (Không rõ là động từ hay tính từ).
  • isOpen, isDisabled, isValid.

3. Quy tắc 300 dòng (The 300-Line Rule)

Nếu một file vượt quá 300 dòng, nó đang làm quá nhiều việc. Dấu hiệu nhận biết:

  1. Quá nhiều useState (trên 5 cái). -> Gom vào useReducer hoặc tách Custom Hook.
  2. JSX quá sâu (> 4 tầng indent). -> Tách Component con (ListItem, Header).
  3. Quá nhiều useEffect. -> Code smells. Logic đang bị phân tán.

4. Tách biệt Logic & UI (Separation of Concerns)

Component chỉ nên lo việc Hiển thị. Logic tính toán phức tạp hãy đẩy ra Custom Hook hoặc Utils.

❌ Trước (Messy):

tsx:
function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(data => {
        const activeUsers = data.filter(u => u.isActive); // Logic lẫn lộn
        setUsers(activeUsers);
        setLoading(false);
      });
  }, []);

  if (loading) return <Spinner />;
  return <ul>{users.map(u => <li>{u.name}</li>)}</ul>;
}

✅ Sau (Clean):

tsx:
// Logic tách riêng
const { users, isLoading } = useActiveUsers(); // Hook tự lo việc fetch và filter

if (isLoading) return <Spinner />;
return <UserTable data={users} />;

5. Các Nguyên Tắc Vàng Khác (Design Principles)

A. Container vs Presentational (Smart vs Dumb)

Tương tự như tách Logic & UI.

  • Smart (Container): UserProfileContainer. Nhiệm vụ: Gọi API, xử lý data, truyền props. Không có CSS phức tạp.
  • Dumb (Presentational): UserProfileCard. Nhiệm vụ: Nhận props user và hiển thị ảnh, tên. Không biết API là gì. Tái sử dụng được ở bất cứ đâu.

B. Single Source of Truth (SSOT)

Đừng bao giờ sync state thủ công. ❌ Bad:

tsx:
const [fullName, setFullName] = useState(firstName + lastName);
useEffect(() => setFullName(firstName + lastName), [firstName, lastName]);

✅ Good (Derived State):

tsx:
const fullName = firstName + lastName; // Tự động cập nhật khi render

C. Inversion of Control (IoC)

Đừng để component con quyết định quá nhiều. Hãy để cha quyết định. ❌ Bad: <Card type="user" /> (Card phải check if type === user thì render avatar). ✅ Good: <Card>{avatar}</Card> (Card chỉ render children. Cha muốn truyền avatar hay icon gì tùy ý).

D. Colocation (Component ở đâu, Logic ở đó)

Đừng vứt file test sang thư mục __test__ xa xôi. Hãy để: Button.tsx, Button.test.tsx, Button.module.css nằm cạnh nhau trong cùng 1 folder Button/. Khi bạn xóa Button -> Xóa cả folder là sạch sẽ.

Kết luận

Code đẹp không phải là code ngắn nhất. Code đẹp là code mà người khác đọc vào hiểu ngay logic mà không cần comment. Hãy viết code cho con người đọc trước, máy tính đọc sau.

Quảng cáo
mdhorizontal