30 câu hỏi React từ render đến production
Trả lời bằng identity, render/commit/effect semantics, user experience và evidence. Mỗi câu nên nêu mental model, failure mode và cách kiểm chứng thay vì chỉ đọc tên API.
Render, state và effects
1. Render khác commit như thế nào?
Render gọi component để tính React tree; nó phải pure và có thể bị lặp, pause hoặc discard. Commit là lúc React áp thay đổi DOM, cập nhật ref và lên lịch Effects.
Side effect trong render phá model này vì một render bị bỏ vẫn có thể đã gửi request hoặc mutate bên ngoài. Event handler và Effect mới là nơi thực thi side effect theo đúng nguyên nhân/lifecycle.
2. React preserve hoặc reset state dựa trên điều gì?
State gắn với vị trí trong returned tree, component type và key. Cùng type/key ở cùng vị trí giữ state; đổi type hoặc key reset subtree.
Key cần stable và xuất phát từ identity dữ liệu. Có thể dùng key có chủ đích để reset form khi chuyển entity, nhưng không tạo key ngẫu nhiên mỗi render.
3. Vì sao index key và random key nguy hiểm?
Khi insert, delete hoặc reorder, index trỏ sang entity khác nên input state, selection và DOM reuse có thể gắn nhầm item. Random key làm mọi item remount ở mỗi render, mất focus/state và tăng work.
Chỉ dùng index khi collection thực sự tĩnh, không reorder và item không có identity riêng.
4. “State là snapshot” nghĩa là gì?
Mỗi render và handler được tạo từ render đó nhìn một bộ props/state cố định. Setter enqueue render mới; nó không đổi local variable trong handler đang chạy.
Điều này giải thích stale closure và vì sao async callback có thể thấy value cũ. Dùng functional updater khi next state phụ thuộc previous state.
English answer: React state is a snapshot for a particular render. Updating state schedules another render; it does not change the value captured by the current closure. Stable keys let React preserve component identity correctly across list changes.
5. Gọi setCount(count + 1) ba lần cho kết quả gì?
Ba call cùng đọc một snapshot và thường queue cùng replacement, nên count chỉ tăng một. Ba functional updater setCount(c => c + 1) compose qua queue và tăng ba.
React batch updates; đừng dựa vào việc setter render ngay giữa handler.
6. Khi nào nên dùng reducer?
Dùng reducer khi nhiều event tác động lên state liên quan, transition có invariant hoặc cần test transition table tập trung. Action nên mô tả domain event; reducer phải pure.
Reducer không cần thiết cho vài field độc lập đơn giản và cũng không tự sửa model nhiều boolean tạo impossible states.
7. Vì sao không mutate state?
React dùng identity và snapshot để biết dữ liệu đổi; mutation làm previous render, memo/cache và alias cùng trỏ graph đã bị thay đổi. Kết quả là update có thể không render hoặc history không còn đáng tin.
Tạo object/array mới cho changed path; shallow spread không clone nested object nên phải copy đúng tầng hoặc normalize.
8. Khi nào không cần Effect?
Không cần cho derived calculation, event-specific action, reset theo key hay thông báo parent trong cùng action. Những việc đó thuộc render/state model/event handler.
Effect dành cho synchronization với external system do component hiện diện: subscription, timer, browser API, widget hoặc connection.
9. Chọn dependency array thế nào?
Liệt kê mọi reactive value Effect đọc. Dependency là hệ quả của code, không phải knob scheduling để tùy chọn.
Nếu object/function identity gây rerun, restructure: đưa construction vào Effect, constant ra module, tách process hoặc ổn định input hợp lý. Không suppress exhaustive-deps để che stale closure.
10. Vì sao Effect có thể chạy hai lần trong development?
Strict Mode chạy thêm setup → cleanup → setup để tìm impurity và cleanup thiếu. Production không có chu kỳ kiểm tra này, nhưng code phải hoạt động giống nhau về mặt user-observable.
Không dùng ref để chặn lần setup thứ hai; sửa cleanup đối xứng và idempotent.
- Why is submitting a form in an Effect usually incorrect? — Vì submit là interaction cụ thể, nên event/action phải sở hữu nó thay vì việc component được hiển thị.
- What does it mean for an Effect to re-synchronize without unmounting? — Dependency đổi làm cleanup configuration cũ rồi setup configuration mới trong cùng component instance.
- Why can suppressing exhaustive-deps introduce stale behavior? — Closure tiếp tục đọc reactive value của render cũ nhưng Effect không được yêu cầu chạy lại.
- How does Strict Mode expose missing cleanup? — Chu kỳ setup → cleanup → setup làm duplicate listener/resource xuất hiện ngay trong development.
- When should a value be read non-reactively? — Dùng event handler cho interaction; dùng Effect Event trong trường hợp Effect cần đọc latest value nhưng value đó không định nghĩa synchronization process.
Hooks, data và UX
11. Ref khác state thế nào?
Cả hai tồn tại qua render, nhưng đổi ref không trigger render. State dùng cho dữ liệu quyết định UI; ref dùng cho DOM node, timer ID, external handle hoặc mutable value chỉ event/effect cần.
Không đọc/ghi ref trong render ngoài initialization predictable vì render cần pure.
12. Custom Hook có chia sẻ state không?
Không. Nó chia sẻ logic; mỗi call có state và Effects riêng. Hai caller chỉ share state khi Hook đọc cùng Context, module singleton, cache hoặc external store.
Contract Hook nên diễn tả inputs, outputs, pending/error và cleanup thay vì che failure/lifecycle quan trọng.
13. Vì sao có Rules of Hooks?
React ánh xạ Hook state theo call order ổn định. Conditional, loop hoặc nested callback có thể đổi order giữa renders, khiến state slot ghép sai.
Chỉ gọi Hook ở top level function component hoặc custom Hook; dùng eslint plugin để kiểm rules và dependencies.
14. Context ảnh hưởng performance ra sao?
Consumer đọc Context re-render khi provider value đổi theo Object.is. Một object/function mới mỗi render hoặc Context high-frequency quá rộng có thể fan-out update.
Colocate state, split contexts theo responsibility/frequency và memo provider value khi có evidence. Context là transport, không tự cung cấp selectors/cache/state architecture.
15. Server state khác client state như thế nào?
Server state do remote system sở hữu, có freshness, cache, invalidation, retry, concurrency và authorization. Client state thuộc UI/session như dialog, selection hoặc local draft.
Framework loader/query cache thường phù hợp server state hơn copy response vào global/local store, vì bản copy tạo stale source thứ hai.
16. Data fetching trong Effect có caveat gì?
Nó bắt đầu sau commit, không preload server HTML, dễ tạo waterfall, duplicate request và cần tự quản cache/race/loading/error. Manual fetch phải abort hoặc ignore stale result.
Ưu tiên data API của framework/router hoặc query cache khi cần SSR, dedupe, prefetch và revalidation.
17. Optimistic update cần gì để an toàn?
Cần snapshot/optimistic identity, rollback hoặc reconcile bằng response server, xử lý concurrent mutations và command idempotency. Timeout có thể là unknown outcome, không đồng nghĩa server chưa thực hiện.
Optimistic UI không bỏ server validation, authorization hay conflict handling; luôn biểu thị pending/failure nếu cần.
18. Suspense làm gì và không làm gì?
Boundary hiển thị fallback khi child suspend từ data/code source có tích hợp Suspense; placement quyết định reveal UX. Fetch tùy ý trong Effect không tự activate Suspense.
Transition có thể giữ content hiện tại trong non-urgent update, nhưng Suspense không thay cache, cancellation hoặc error boundary.
19. Error Boundary bắt loại lỗi nào?
Nó bắt render/lifecycle errors của descendants, hiển thị fallback và report. Nó không tự bắt event handler, async callback/timer, SSR, lỗi chính boundary hoặc error đã được catch nơi khác.
Đặt theo recoverable product region và thiết kế reset/retry; root boundary chỉ là safety net cuối.
20. UI route guard có phải security boundary không?
Không. Nó chỉ cải thiện navigation UX. Server/API phải authenticate và authorize resource/action cho mọi request, kể cả khi attacker gọi trực tiếp.
Client route, bundle và storage không được chứa secret; ẩn nút không thay quyền server.
Performance, testing và production
21. Re-render có luôn là vấn đề performance không?
Không. Render có thể rẻ và không tạo DOM update. Chậm có thể do network waterfall, long task, layout/paint, huge DOM hoặc bundle.
Profile critical interaction bằng React DevTools và browser trace, rồi xác nhận bằng production/field metrics.
22. Khi nào dùng memo, useMemo, useCallback?
Dùng khi measured expensive subtree/calculation hoặc API cần stable identity. memo so props, useMemo cache result, useCallback cache function.
Correctness không được phụ thuộc cache. Một always-new prop phá memo; comparator đắt hoặc sai có thể tệ hơn render.
English answer: I do not apply memoization by default. I first profile the interaction, identify expensive repeated rendering, then stabilize only the values or component boundaries that materially reduce work. Memoization also has comparison and complexity costs.
23. useTransition giải quyết vấn đề gì?
Nó đánh dấu state update non-urgent có thể bị interrupt và cung cấp pending state, giúp urgent interaction như typing phản hồi trước.
Không đặt controlled input value trong transition, không dùng nó thay debounce/cancellation và không làm network request nhanh hơn.
24. Tối ưu large list thế nào?
Dùng pagination/windowing, stable entity keys, state colocation và row nhẹ; tối ưu image/layout. Chọn overscan theo scroll UX.
Đo render/scroll và kiểm keyboard, screen reader, focus, find-in-page vì virtualization có accessibility/product trade-off.
25. React Compiler có thay toàn bộ memo thủ công không?
Compiler có thể tự memo code tương thích dựa trên purity và Rules of React, giảm nhu cầu manual memo. Nó có thể skip vùng không chứng minh an toàn.
Nó không sửa state ownership, network, DOM size hay bundle. Rollout dần, xem diagnostics và profile production trước/sau; giữ manual contract khi có lý do đo được.
26. Một component test tốt có đặc điểm gì?
Query theo role/name/label, tương tác như user và assert output, focus, accessible status hoặc network contract. Mock protocol boundary thay vì Hook internals.
Test phải chống refactor markup không đổi behavior; async chờ condition observable, không sleep cố định.
27. Controlled và uncontrolled form khác nhau thế nào?
Controlled dùng React state làm source of truth và phù hợp conditional UI/validation phối hợp. Uncontrolled để DOM giữ value, đọc bằng FormData/ref và có thể giảm update.
Chọn theo workflow; không chuyển mode trong lifetime. Server vẫn validate, còn pending/error/touched là UX state riêng.
28. Nguyên nhân và cách xử lý hydration mismatch?
Server/client initial output khác vì time zone, random, browser-only branch, data snapshot lệch, invalid HTML hoặc DOM bị extension sửa. Serialize cùng data, render deterministic, dùng useId và chuyển browser-only work sang Effect/client boundary.
suppressHydrationWarning là escape hatch hẹp, không sửa nguyên nhân và không nên dùng đại trà.
29. Kiểm thử accessibility cần những lớp nào?
Kiểm semantic role/name/state, keyboard và focus, contrast/reflow/reduced motion, live regions và error association. Kết hợp lint, DOM integration, browser accessibility tree và manual screen reader cho flow quan trọng.
Automation không đánh giá đầy đủ reading order, announcement timing hoặc cognitive UX.
30. React production observability nên có gì?
RUM Web Vitals, route/API timing, Error Boundary và unhandled errors, release/version, source map có kiểm soát, correlation privacy-safe và business success metrics.
So canary với baseline theo percentile/cohort; redact PII/credential và có rollback guardrail rõ.