Base.vn Prep

2026-05-19 · Middle-level

FRONTEND

Frontend Deep Dive

React Hooks · TanStack Query · Next.js patterns · Performance. Stack focus: Next.js 16 + React 19 + MUI v7 + Tailwind v4.

1 · React Hooks Essentials

useState

Local state. Functional update setX(prev => prev+1) khi depend giá trị cũ.

useEffect

Side effect sau render. Cleanup return function. Empty deps = mount-only.

useMemo

Cache giá trị tính nặng. KHÔNG dùng default — chỉ khi profiler thấy slow.

useCallback

Cache function ref. Chỉ cần khi pass xuống React.memo child.

useRef

Mutable value KHÔNG re-render. DOM ref, timer id, prev value.

Custom hook

Tên use*. Encapsulate logic + state. Reusable.

// Custom hook
function useDebounce<T>(value: T, delay = 300): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer); // cleanup
  }, [value, delay]);
  return debounced;
}

Pitfall: useEffect deps thiếu → stale closure. Object/array literal trong deps → effect mỗi render. Cleanup quên → memory leak. useMemo mọi nơi → tăng RAM.

2 · TanStack Query Patterns

Vì sao: Cache + dedup + background refetch + staleTime + optimistic. Tự handle loading/error.

// Query key factory — tránh typo + dễ invalidate
export const taskKeys = {
  all: ['tasks'] as const,
  list: (filters: TaskFilter) => [...taskKeys.all, 'list', filters] as const,
  detail: (id: string) => [...taskKeys.all, 'detail', id] as const,
};

export function useTasks(filters: TaskFilter) {
  return useQuery({
    queryKey: taskKeys.list(filters),
    queryFn: () => api.tasks.list(filters),
    staleTime: 1000 * 60, // 1 min
  });
}

export function useCreateTask() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: api.tasks.create,
    onSuccess: () => qc.invalidateQueries({ queryKey: taskKeys.all }),
  });
}

staleTime

Bao lâu data còn fresh. Trong window không refetch.

gcTime

Bao lâu giữ memory sau khi không dùng. Default 5m.

Optimistic

onMutate set trước, rollback khi error.

Pitfall: queryKey không stable (inline object) → refetch loạn. Quên invalidate sau mutation → stale UI. enabled:false nhưng vẫn render → conditional query phải chuẩn.

3 · Next.js Patterns (App Router)

Server Component (RSC)

Default trong app/. Render server, không state/event, không ship JS. Fetch DB trực tiếp.

Client Component

"use client" directive. Có state/effect. Hydrate trên client.

Server Action

"use server". Form submit trực tiếp tới function server — không cần API route.

Streaming + Suspense

loading.tsx cho route. <Suspense> stream phần data chậm.

// app/tasks/page.tsx — Server Component
export default async function TasksPage() {
  const tasks = await db.task.findMany(); // fetch trực tiếp DB
  return <TaskList tasks={tasks} />; // Client Component
}

// app/tasks/TaskList.tsx
"use client";
import { useState } from 'react';
export function TaskList({ tasks }) {
  const [filter, setFilter] = useState('all');
  return /* interactive UI */;
}

Caching layers: Request memoization (1 req) · Data cache (cross-request) · Full route cache (build-time) · Router cache (client). revalidateTag / revalidatePath để invalidate.

Pitfall: Pass non-serializable (function, Date raw) từ Server → Client → error. "use client" bừa → mất benefit RSC. Quên revalidate sau mutation → user thấy stale.

4 · Performance Optimization

Mantra: Đo trước, optimize sau. React Profiler + Lighthouse + Chrome Performance.

Code splitting

dynamic(() => import('./Heavy')) · route-based + component-based.

Image optimization

Next.js <Image> — auto WebP/AVIF, lazy load, responsive srcset.

Virtual list

10k items → TanStack Virtual / react-window. Chỉ render visible.

Bundle analysis

@next/bundle-analyzer — thấy package nào nặng nhất.

Re-render reduce

State local · split component · React.memo + stable props · Context split.

Core Web Vitals

LCP < 2.5s · INP < 200ms · CLS < 0.1. Track real-user qua Vercel Analytics.

Pitfall: Premature optimization (memoize mọi component). Bundle import full lodash thay vì lodash/get. CSS-in-JS runtime nặng — pick Tailwind / CSS Modules cho perf.

← Backend Next: Principles →