Skip to main content

Hooks & useEffect

Quy tắc dùng useEffect (dependencies, tránh vòng lặp vô hạn), memoization (useMemo, useCallback), event handler, luôn return giá trị và không dùng underscore cho method "nội bộ" (theo React/JSX Style Guide).

useEffect​

Khai báo đúng dependencies​

Tất cả useEffect phải khai báo đúng dependency array. Biến/hàm dùng trong effect mà đến từ props/state phải nằm trong mảng dependency để tránh stale closure và hành vi khó đoán.

// ❌ Tránh: thiếu dependency
useEffect(() => {
fetchUser(userId);
}, []);

// ✅ Tốt
useEffect(() => {
fetchUser(userId);
}, [userId]);

Hàm fetchUser từ props/context nên đưa vào dependency hoặc wrap bằng useCallback ở nơi định nghĩa.

Không gây vòng lặp render vô hạn​

Effect không được gây vòng lặp vô hạn: tránh set state (hoặc trigger re-render) trong effect trong khi dependency là object/array tạo mới mỗi lần render.

// ❌ Tránh: options mới mỗi lần → effect chạy lại → setState → render → effect lại...
useEffect(() => {
setResult(fetchSomething(options));
}, [options]);

// ✅ Tốt: dependency ổn định (primitive hoặc memoized)
const optionsStable = useMemo(() => ({ page, limit }), [page, limit]);
useEffect(() => {
setResult(fetchSomething(optionsStable));
}, [optionsStable]);

Memoization​

Dùng useMemo / useCallback khi cần​

Khi cần tránh tính toán lại hoặc tránh tạo hàm mới mỗi lần render (truyền xuống component con hoặc dùng trong dependency của useEffect):

  • useMemo — cho giá trị (object, array) phụ thuộc props/state.
  • useCallback — cho hàm (event handler, callback truyền xuống con).
const sortedList = useMemo(
() => [...items].sort((a, b) => a.name.localeCompare(b.name)),
[items],
);

const handleSubmit = useCallback(() => {
submitForm(formData);
}, [formData]);

Chỉ dùng khi thực sự cần (performance hoặc dependency ổn định), tránh lạm dụng.


Event handler​

Arrow function khi cần closure biến local​

Khi cần truyền thêm dữ liệu vào handler (ví dụ item, index trong map), có thể dùng arrow function trong render. Lưu ý: nếu truyền xuống component tối ưu (PureComponent / React.memo), arrow tạo mới mỗi lần render có thể gây re-render không cần thiết — khi đó nên dùng useCallback hoặc truyền data qua prop khác.

function ItemList({ items, onItemAction }: Props) {
return (
<ul>
{items.map((item, index) => (
<Item key={item.id} onClick={(e) => onItemAction(e, item.id, index)} />
))}
</ul>
);
}

Handler ổn định: dùng useCallback​

Handler truyền xuống component được memo (React.memo) hoặc dùng trong dependency của useEffect nên wrap bằng useCallback để reference ổn định.

const handleClick = useCallback(() => {
doSomething(id);
}, [id]);

return <ExpensiveChild onClick={handleClick} />;

Return và đặt tên​

Component luôn return giá trị​

Component (function) phải return một giá trị (JSX, null, ...). Không gọi function rồi bỏ qua kết quả.

// ❌ Tránh
function MyComponent() {
<div />;
}

// ✅ Tốt
function MyComponent() {
return <div />;
}

Không dùng underscore cho method "nội bộ"​

Không dùng tiền tố underscore (_handleSubmit) để thể hiện method "private". Trong JavaScript mọi thứ đều public; underscore không tạo private. Đặt tên bình thường (ví dụ handleSubmit, onSubmit) cho handler.

// ❌ Tránh
function Form() {
const _onClickSubmit = () => {
/* ... */
};
return <button onClick={_onClickSubmit}>Submit</button>;
}

// ✅ Tốt
function Form() {
const onClickSubmit = () => {
/* ... */
};
return <button onClick={onClickSubmit}>Submit</button>;
}