Skip to main content

Classes & Modules

Classes​

Theo convention team: chỉ dùng Function Component trong React, không dùng Class Component. Phần Classes áp dụng khi cần class thuần (service, utility) hoặc tham khảo.

Luôn dùng class; tránh chỉnh prototype trực tiếp​

// ❌ Tránh
function Queue(contents: unknown[] = []) {
this.queue = [...contents];
}
(Queue as any).prototype.pop = function () {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
};

// ✅ Tốt
class Queue {
queue: unknown[];
constructor(contents: unknown[] = []) {
this.queue = [...contents];
}
pop(): unknown {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
}
}

Dùng extends cho kế thừa​

// ✅ Tốt
class PeekableQueue extends Queue {
peek(): unknown {
return this.queue[0];
}
}

Method có thể return this để chain​

// ✅ Tốt
class ApiBuilder {
private url = "";
setBaseUrl(url: string) {
this.url = url;
return this;
}
setPath(path: string) {
this.url += path;
return this;
}
build() {
return this.url;
}
}
const url = new ApiBuilder().setBaseUrl("/api").setPath("/users").build();

Constructor rỗng hoặc chỉ gọi super(...args) thì có thể bỏ​

// ❌ Tránh
class Rey extends Jedi {
constructor(...args: unknown[]) {
super(...args);
}
}

// ✅ Tốt
class Rey extends Jedi {
constructor(...args: unknown[]) {
super(...args);
this.name = "Rey";
}
}

Tránh duplicate class members​

// ❌ Tránh
class Foo {
bar() {
return 1;
}
bar() {
return 2; // chỉ cái này có hiệu lực, dễ gây nhầm
}
}

// ✅ Tốt
class Foo {
bar() {
return 1;
}
}

Method instance nên dùng this hoặc chuyển thành static​

// ✅ Tốt: dùng this
class Counter {
value = 0;
increment() {
this.value += 1;
}
}

// ✅ Tốt: static khi không cần instance
class MathUtils {
static clamp(n: number, min: number, max: number): number {
return Math.min(Math.max(n, min), max);
}
}

Modules​

Luôn dùng module (import/export), không dùng hệ module không chuẩn​

// ❌ Tránh
const config = require("./config");
module.exports = config.default;

// ✅ Tốt
import { es6 } from "./AirbnbStyleGuide";
export default es6;

Không dùng wildcard import​

// ❌ Tránh
import * as AirbnbStyleGuide from "./AirbnbStyleGuide";
const es6 = AirbnbStyleGuide.es6;

// ✅ Tốt
import AirbnbStyleGuide from "./AirbnbStyleGuide";
import { es6 } from "./AirbnbStyleGuide";

Chỉ import từ cùng một path ở một chỗ​

// ❌ Tránh
import foo from "utils";
// ... code ...
import { named1, named2 } from "utils";

// ✅ Tốt
import foo, { named1, named2 } from "utils";

// ✅ Tốt: nhiều named
import foo, { named1, named2 } from "utils";

Không export binding có thể bị gán lại (mutate); export const​

// ❌ Tránh: có thể gán lại từ bên ngoài
let config = { env: "dev" };
export { config };

// ✅ Tốt
const config = { env: "dev" };
export { config };

Import đặt trên cùng, không xen lẫn với code khác​

// ❌ Tránh
const x = 1;
import { bar } from "./bar";
function fn() {
return bar();
}

// ✅ Tốt
import { bar } from "./bar";
const x = 1;
function fn() {
return bar();
}

Import nhiều dòng: thụt giống object/array, có trailing comma​

// ❌ Tránh
import {
longLongNameA,
longLongNameB,
longLongNameC,
longLongNameD,
} from "path";

// ✅ Tốt
import {
longLongNameA,
longLongNameB,
longLongNameC,
longLongNameD,
} from "path";

Không ghi extension file trong import (.js, .ts, .tsx)​

// ❌ Tránh
import foo from "./foo.ts";
import bar from "./bar.tsx";

// ✅ Tốt
import foo from "./foo";
import bar from "./bar";

Không import trực tiếp từng file​

Ưu tiên import từ barrel file (index) của thư mục thay vì path dài đến từng file.

Tránh:

import UpdateModal from "@/app/[locale]/orgs/[orgId]/vehicles/components/UpdateModal";
import Modal from "@/components/molecules/Modal/Modal.tsx";
import useVehicles from "@/hooks/useVehicles.ts";

Đúng:

import { UpdateModal } from "./components";
import { Modal } from "@/components/molecules";
import { useVehicles } from "@/hooks";

Lợi ích: khi đổi tên hoặc di chuyển file trong components, chỉ cần sửa index.ts; các file import không phải sửa path dài.

Không import inline trong function​

Khai báo import luôn ở đầu file, không đặt trong function hoặc block.

Tránh:

function handleClick() {
const { format } = await import("date-fns");
return format(new Date(), "yyyy-MM-dd");
}

Đúng:

import { format } from "date-fns";

function handleClick() {
return format(new Date(), "yyyy-MM-dd");
}

(Dynamic import có thể dùng khi cần code-splitting, nhưng nên đặt ở top level hoặc dùng React.lazy cho component.)

Không import từ app folder​

Không dùng path bắt đầu từ @/app (hoặc tương đương) để import component/hook từ một route vào route khác. Logic dùng chung nên nằm ở components, hooks, lib và được import từ đó.

// ❌ Tránh
import UpdateModal from "@/app/[locale]/orgs/[orgId]/vehicles/components/UpdateModal";

// ✅ Tốt
import { UpdateModal } from "@/components";