Tổng hợp 75 câu hỏi phỏng vấn State Management kèm đáp án chi tiết, song ngữ Việt–Anh — sắp xếp từ cơ bản đến nâng cao. Ôn luyện miễn phí, tự động lưu tiến độ.
RTK là cách duy nhất được khuyến nghị để viết Redux hiện đại — createSlice gộp actions+reducer, Immer cho phép write mutable syntax, RTK Query thay thế thunk cho data fetching. Redux Toolkit (RTK) là bộ công cụ chính thức và là cách duy nhất…
createSlice gộp actions, action types, và reducer vào một chỗ — Immer bên trong cho phép viết mutable syntax mà vẫn immutable thật sự. createSlice là API quan trọng nhất của RTK, nhận object gồm name (prefix cho action types), initialState, và reducers (object chứa…
createAsyncThunk tạo thunk action creator cho async logic (gọi API) và tự dispatch ba lifecycle action: pending (bắt đầu → bật loading), fulfilled (thành công → cập nhật data), rejected (thất bại → hiện lỗi). Xử lý trong slice bằng extraReducers với builder pattern: -…
createSelector memoize kết quả selector — chỉ recompute khi input thay đổi, tránh re-render không cần thiết khi filter/map trả về reference mới. Selector là function nhận toàn bộ Redux state và trích xuất/tính toán phần data cần thiết, ví dụ: const selectActiveTodos = state…
Middleware là function nằm giữa dispatch(action) và reducer, có thể inspect, modify, delay, hoặc cancel action trước khi đến reducer. Flow: dispatch → middleware1 → middleware2 → reducer. Middleware phổ biến: redux-thunk (cho phép dispatch function thay vì object, xử lý async — RTK include…
useSelector nhận selector function, subscribe vào Redux store, và dùng strict === comparison (reference equality) để quyết định có re-render không. Lưu ý quan trọng: nếu selector trả về object/array mới mỗi lần gọi — ví dụ useSelector(s = s.items.filter(...)) — reference luôn khác nên…
Zustand là thư viện quản lý state nhỏ gọn cho React, không yêu cầu boilerplate như Redux — không cần actions, reducers, dispatch, hay Provider wrapper. API chỉ gồm hàm create trả về một hook, state và actions định nghĩa cùng chỗ. Bundle size ~1KB…
Dùng create từ 'zustand', truyền vào initializer nhận (set, get) và trả về object chứa cả state lẫn action trong cùng một chỗ. set() merge partial state (không cần spread toàn bộ như Redux), get() đọc state hiện tại từ bên trong action. Store trả…
Action trong Zustand là function thường sống cùng state trong store object — không action type, không dispatch, không reducer. Ba pattern hay dùng: Nhiều lần cập nhật state trong cùng một lời gọi set được batch tự động. Lưu ý: set chỉ shallow merge…
Hai kiến trúc khác nhau từ gốc. Redux đi theo Flux: action object → dispatch → middleware → reducer → state mới. Zustand không có khái niệm action type hay reducer — chỉ là function gọi set(). Redux bắt buộc kèm theo Provider wrapper, combineReducers,…
Luôn dùng selector cụ thể — gọi useStore() không selector là subscribe toàn bộ store, re-render khi bất kỳ state nào đổi. Zustand so sánh bằng === nên selector trả primitive thì không có vấn đề; rắc rối chỉ xảy ra khi selector tạo object/array…
Zustand middleware wrap store creator với higher-order function — devtools, immer, persist là ba middleware phổ biến nhất; compose bằng cách nest lồng nhau, devtools ở ngoài cùng. Zustand middleware dùng pattern higher-order function wrap store creator. Import từ 'zustand/middleware'. Compose nhiều middleware bằng cú…
Middleware persist từ 'zustand/middleware' serialize state thành JSON, lưu vào storage và rehydrate khi app khởi động. - name là storage key, bắt buộc. - partialize chọn phần state cần lưu — bỏ qua loading/error state cho gọn. - storage đổi engine: sessionStorage, hoặc AsyncStorage…
React Query (nay là TanStack Query v5) là thư viện quản lý server state cho React, giải quyết bài toán mà useState + useEffect không làm tốt: fetching, caching, synchronizing và updating data từ API. Ví dụ thực tế: không cần React Query, bạn phải…
useQuery là hook chính để đọc data, nhận 2 tham số bắt buộc: queryKey (mảng định danh duy nhất, ví dụ ['todos', { status: 'active' }]) và queryFn (async function trả về data, ví dụ () = fetch('/api/todos').then(r = r.json())). Trả về object gồm: data…
useMutation xử lý write operations (POST/PUT/DELETE) — khác useQuery chỉ đọc. Cung cấp mutate() (fire-and-forget) hoặc mutateAsync() (trả về Promise). States: isPending, isError, isSuccess, data, error. Optimistic update flow: (1) onMutate: cancel ongoing queries, snapshot data cũ, set cache mới ngay → UI cập nhật…
Cache invalidation là cơ chế đánh dấu data đã cũ (stale) để trigger refetch. Cách dùng: queryClient.invalidateQueries({ queryKey: ['todos'] }) — tất cả queries có key bắt đầu bằng 'todos' bị đánh dấu stale. Nếu component đang mount thì refetch ngay, nếu không thì refetch…
staleTime kiểm soát khi nào refetch; gcTime kiểm soát khi nào xoá cache — staleTime nên ≤ gcTime. Hai config quan trọng nhất quyết định caching behavior. staleTime (mặc định 0): thời gian data được coi là 'fresh' — trong khoảng này component mount mới…
Có hai loại state khác nhau về bản chất: - Client state (theme, sidebar đang mở, nội dung form) — client sở hữu hoàn toàn, cập nhật là thấy ngay, và không bao giờ "cũ". - Server state (profile user, danh sách todo, catalog sản…
QueryClient là singleton quản lý toàn bộ cache và config. Tạo BÊN NGOÀI component để không bị dựng lại sau mỗi render. defaultOptions áp cho mọi query/mutation trừ khi override ở từng query. - Mặc định retry 3 lần với exponential backoff; ví dụ trên…
refetchOnWindowFocus (mặc định bật) refetch tất cả stale queries khi user quay lại tab — tắt khi data ít thay đổi hoặc refetch gây UX kém. refetchOnWindowFocus là behavior mặc định: khi user chuyển tab rồi quay lại, React Query tự động refetch tất cả…
React Query v5 đổi cách xử lý lỗi so với v4: onError toàn cục trong defaultOptions đã bị gỡ, thay bằng throwOnError hoặc meta. - retry: (failureCount, error) = error.status !== 401 && failureCount < 3 để không retry lỗi auth. - throwOnError: true đẩy…
QueryKey là nền của kiến trúc cache. Ba nguyên tắc: đưa MỌI dependency dùng trong queryFn vào key (['todos', userId, { status, page, sort }]); dùng array chứ đừng dùng string thuần, vì array mới invalidate fuzzy được; xếp từ tổng quát đến cụ thể…
Jotai là thư viện atomic state management tương tự Recoil nhưng nhỏ gọn hơn (~3KB vs ~21KB) và API đơn giản hơn đáng kể. Điểm khác biệt chính: Jotai không yêu cầu key string duy nhất cho mỗi atom (Recoil bắt buộc, dễ gây conflict…
Primitive atom: const countAtom = atom(0) — trong component dùng useAtom(countAtom) trả về [value, setValue]. Derived read-only: const doubleAtom = atom(get = get(countAtom) 2) — dùng useAtomValue. Derived read-write: atom(get = get(baseAtom), (get, set, newVal) = set(baseAtom, newVal 2)). atomWithStorage('key', defaultVal) từ jotai/utils tự đồng…
Provider-less là mặc định: Jotai dùng global WeakMap store nên mọi component share cùng atom value mà không phải bọc Provider. Khi nào cần Provider: - Testing — <Provider store={createStore()} tạo store cô lập cho từng test, tránh state rò rỉ giữa các test. -…
Context API: state đơn giản, ít thay đổi (theme, auth, locale) — không cần cài thêm gì. Zustand: nhẹ (~1KB), ít boilerplate, API đơn giản, phù hợp đa số projects. Redux Toolkit: enterprise, complex state cần middleware (async thunks, saga), DevTools mạnh, team lớn cần…
createSlice kết hợp action creators + reducer trong cùng một file, dùng Immer nên có thể mutate state trực tiếp.
QueryClientProvider là React Context Provider mang instance QueryClient xuống toàn bộ cây component. QueryClient chính là bộ não giữ cache, trạng thái query và config — mọi useQuery/useMutation bên dưới đều cần truy cập nó. Trong Next.js App Router: đặt provider ở root layout nhưng…
isLoading = true chỉ lần fetch đầu chưa có cache (show skeleton toàn trang); isFetching = true MỌI KHI đang fetch kể cả background refetch (show subtle indicator). Đây là câu phỏng vấn rất phổ biến. isLoading (hay status === 'pending'): true CHỈ khi fetch…
initialData được treat như real cached data và ảnh hưởng staleTime; placeholderData là temporary fill UI (isPlaceholderData=true), không persist vào cache — dùng placeholderData: prev = prev để giữ data trang trước khi paginate. Hai option này phục vụ mục đích khác nhau. placeholderData: data…
select transform data sau fetch trước khi component nhận — cache vẫn lưu raw data; component chỉ re-render khi select result thay đổi; stabilize function với useCallback để tránh re-render không cần thiết. select là transform function chạy sau khi queryFn trả về data, trước…
Parallel queries là kỹ thuật fetch nhiều API endpoints cùng lúc thay vì tuần tự (waterfall). Cách 1: gọi nhiều useQuery hooks trong cùng component — React Query tự động chạy chúng parallel. Cách 2: useQueries hook nhận mảng query configs, hữu ích khi số…
queryFn nhận signal (AbortSignal) trong context — truyền signal xuống fetch là bật được huỷ request. Khi component unmount hoặc queryKey đổi, React Query abort signal và trình duyệt huỷ request đang chờ. Huỷ xảy ra trong ba tình huống: - Component unmount trước lúc…
keepPreviousData (v4) đổi thành placeholderData: (prev) = prev ở v5: khi queryKey đổi (trang 1 → trang 2), React Query giữ data trang cũ trên màn hình thay vì xoá đi và hiện loading. Cờ isPlaceholderData cho biết đang hiển thị data cũ. - Phân…
enabled: !!dependency là pattern chuẩn cho dependent queries — query không fetch cho đến khi dependency có giá trị; enabled: false + refetch() cho lazy queries triggered by user action. enabled option kiểm soát khi nào query được phép chạy — là boolean hoặc function…
Redux phù hợp cho complex global state cần time-travel debug; RTK là cách dùng Redux 2025. Redux là thư viện quản lý state tập trung cho JavaScript, thường dùng với React. Nó lưu toàn bộ state ứng dụng trong một store duy nhất, giúp dễ…
Redux dựa trên ba nguyên tắc: 1. Single source of truth — toàn bộ state nằm trong một store duy nhất. Nhờ vậy data đồng bộ giữa các component, và serialize rồi restore lại rất dễ (lưu state xuống localStorage rồi hydrate khi reload). 2.…
Action là plain JavaScript object mô tả sự kiện xảy ra trong ứng dụng, bắt buộc có trường type (string định danh) và thường có payload chứa dữ liệu. Ví dụ: { type: 'cart/addItem', payload: { id: 1, name: 'iPhone', qty: 1 } }. Quy…
Reducer là pure function nhận (state, action) và trả về state mới — không được mutate state trực tiếp mà phải tạo bản sao mới. Ví dụ đúng: case 'ADDTODO': return [...state, action.payload] thay vì state.push(action.payload). Pure function nghĩa là không có side effects, không…
Store là object trung tâm giữ toàn bộ state tree của app, tạo bằng configureStore() (RTK) hoặc createStore() (bản cũ). Ba phương thức chính: - getState() trả về state hiện tại. - dispatch(action) đẩy action qua middleware rồi tới reducer để cập nhật state. -…
Chia theo nguồn sở hữu dữ liệu, không chia theo "global hay local". Bốn nhóm hay gặp: - Server state — dữ liệu thuộc về backend, client chỉ giữ bản sao có thể cũ: danh sách đơn hàng, hồ sơ user. Đặc trưng: bất đồng…
Phần lớn dự án vừa và nhỏ chưa cần. Thứ tự leo thang nên theo: 1. useState ngay tại component dùng nó. 2. Lift state up — đưa lên cha chung gần nhất khi hai component anh em cần cùng dữ liệu. 3. Truyền props…
Context là cơ chế truyền giá trị xuống cây component (dependency injection), không phải state manager. Bản thân nó không lưu state — state vẫn nằm ở useState/useReducer của Provider. Ba thứ nó không có: - Không có selector. Mọi consumer của một context re-render…
Lưu trên URL query param, không phải useState hay store global. State đó thuộc về URL vì nó mô tả "user đang xem cái gì". Lợi ích cụ thể: - Chia sẻ được: copy link gửi đồng nghiệp ra đúng kết quả đã lọc. -…
Nguyên nhân: selector trả về object/array mới mỗi lần chạy. Zustand so sánh kết quả selector bằng Object.is, object mới luôn khác object cũ nên component re-render, selector lại chạy, tạo vòng lặp. Ba cách sửa: Cách 3 là chỉ lấy đúng thứ cần: nếu…
Store của Zustand là một object độc lập với React, nên gọi trực tiếp được ở bất kỳ đâu: Lưu ý: getState() chỉ trả về giá trị tại thời điểm gọi, không phản ứng với thay đổi sau đó — đừng dùng trong render, chỉ…
Vì RTK dùng Immer bọc reducer. Cái bạn nhận không phải state thật mà là draft proxy: mọi thao tác "mutate" được ghi lại, sau đó Immer tạo ra state mới, chỉ copy những nhánh thực sự đổi (structural sharing). Store vẫn nhận về object…
Normalize là lưu dữ liệu dạng bảng phẳng giống database: mỗi loại entity một map byId, kèm mảng allIds giữ thứ tự — thay vì giữ nguyên cây JSON lồng nhau mà API trả về. Vấn đề của nested: - Một user xuất hiện ở…
Mặc định là không. Giá trị đang gõ là state cục bộ, vòng đời bằng vòng đời form, và thay đổi theo từng ký tự. Đưa vào store global gây ba vấn đề: - Mỗi phím gõ dispatch một action → mọi subscriber của store…
Lưu ý: server render không có localStorage — đọc thẳng là ReferenceError, nên phải chặn bằng typeof window và dựng HTML bằng giá trị mặc định; client dựng lại bằng giá trị đã lưu. Hai bản khác nhau → React báo hydration mismatch và có…