Bỏ qua đến nội dung
KhaiziNam Blog KhaiziNam Blog
Quay lại
Read in English

Câu Hỏi Phỏng Vấn ReactJS Junior: 30+ Câu Thực Tế Kèm Đáp Án Chuẩn 2026

Câu hỏi phỏng vấn ReactJS junior: 30+ câu thực tế kèm đáp án chuẩn cho developer 2026

Tổng hợp 30+ câu hỏi phỏng vấn ReactJS junior hay gặp nhất năm 2026 — từ Virtual DOM, hooks, state management đến performance optimization — kèm đáp án chi tiết và ví dụ code giúp bạn tự tin vượt qua mọi vòng technical interview.

Bạn đã học React được vài tháng, build được project, nhưng mỗi lần vào phỏng vấn lại bị hỏi những thứ không có trong tutorial nào bạn từng đọc? “useEffect cleanup function hoạt động thế nào?”, “Tại sao không nên mutate state trực tiếp?”, “Reconciliation là gì?” — đây chính xác là những câu phân biệt người chỉ biết dùng React với người thực sự hiểu React. Bài viết này tổng hợp 30+ câu hỏi phỏng vấn ReactJS junior thực tế nhất, phân loại theo độ khó, kèm đáp án đủ sâu để bạn không chỉ trả lời được mà còn gây ấn tượng với interviewer.

Nội dung bài viết:


1. Bản chất React — Câu hỏi nền tảng bắt buộc phải nắm

1.1 Virtual DOM là gì và tại sao React dùng nó?

Virtual DOM là một bản sao nhẹ của DOM thực, được lưu trong bộ nhớ JavaScript. Khi state thay đổi, React tạo ra một Virtual DOM mới, so sánh với Virtual DOM cũ (quá trình gọi là diffing), tìm ra sự khác biệt tối thiểu, rồi chỉ cập nhật đúng những phần thay đổi trên DOM thực (quá trình gọi là reconciliation).

Tại sao cần làm vậy? Thao tác trực tiếp trên DOM thực rất chậm vì mỗi lần thay đổi DOM, trình duyệt phải tính toán lại layout và repaint. Virtual DOM giảm thiểu số lần và phạm vi thao tác DOM thực xuống mức tối thiểu.

Câu trả lời gây ấn tượng thêm: đề cập rằng React 18 đã introduce Concurrent Rendering — React có thể ưu tiên cập nhật quan trọng hơn (như input của user) trước các cập nhật ít quan trọng hơn (như fetch data), giúp UI luôn responsive.

1.2 Reconciliation hoạt động như thế nào?

Reconciliation là thuật toán React dùng để quyết định phần nào của DOM cần cập nhật. React dùng hai giả định để tối ưu từ O(n³) xuống O(n):

Đây là lý do tại sao dùng index làm key trong list là anti-pattern: khi list reorder, key không thay đổi nhưng content thay đổi — React không biết item đã di chuyển, dẫn đến bug khó tìm.

1.3 JSX là gì — nó có phải HTML không?

JSX là syntax extension cho JavaScript, trông giống HTML nhưng thực ra là syntactic sugar cho React.createElement(). Babel compile JSX thành JavaScript thuần. Ví dụ:

Hello

được compile thành React.createElement(‘div’, {className: ‘box’}, ‘Hello’). Đây là lý do dùng className thay vì class — vì class là reserved keyword trong JavaScript.

2. React Hooks — Phần được hỏi nhiều nhất trong phỏng vấn

2.1 useState — Những điều interviewer hay test

Không chỉ “useState dùng để làm gì” — interviewer sẽ test sâu hơn:

Câu hỏi: Tại sao không nên mutate state trực tiếp?

// SAI — mutate state trực tiếp const [user, setUser] = useState({ name: ‘An’, age: 25 }); user.age = 26; // React không biết state đã thay đổi, không re-render setUser(user); // Vẫn không re-render vì reference không đổi

// ĐÚNG — tạo object mới setUser({ …user, age: 26 }); // Spread tạo object mới → React detect thay đổi → re-render

Câu hỏi: useState cập nhật bất đồng bộ — xử lý thế nào?

// Vấn đề: state cũ trong closure const [count, setCount] = useState(0);

// SAI — dùng stale state const handleClick = () => { setCount(count + 1); // count có thể là giá trị cũ nếu gọi nhiều lần setCount(count + 1); // Vẫn chỉ tăng 1, không phải 2 };

// ĐÚNG — dùng functional update const handleClick = () => { setCount(prev => prev + 1); // Luôn dùng giá trị mới nhất setCount(prev => prev + 1); // Tăng 2 như mong đợi };

2.2 useEffect — Câu hỏi phức tạp nhất về hooks

Dependency array:

// Không có dependency array — chạy sau mỗi render useEffect(() => { console.log(‘Runs every render’); });

// Array rỗng — chạy một lần sau mount (tương đương componentDidMount) useEffect(() => { fetchData(); }, []);

// Có dependency — chạy khi dependency thay đổi useEffect(() => { fetchUser(userId); }, [userId]);

Cleanup function — hay bị hỏi nhất:

useEffect(() => { const subscription = dataStream.subscribe(handler); const timer = setInterval(tick, 1000);

// Cleanup: chạy trước khi effect chạy lại, hoặc khi component unmount return () => { subscription.unsubscribe(); // Tránh memory leak clearInterval(timer); // Tránh timer chạy sau khi unmount }; }, [dataStream]);

Câu hỏi hay gặp: “Khi nào cleanup function chạy?” — Trả lời: cleanup chạy trong hai trường hợp: (1) trước khi effect chạy lại do dependency thay đổi, (2) khi component unmount. Nếu thiếu cleanup với subscription hay setInterval, component unmount rồi nhưng callback vẫn chạy → memory leak và lỗi “Can’t perform a React state update on an unmounted component”.

2.3 useCallback và useMemo — Khi nào dùng, khi nào không?

// useMemo — cache kết quả tính toán nặng const expensiveValue = useMemo(() => { return heavyComputation(data); // Chỉ tính lại khi data thay đổi }, [data]);

// useCallback — cache function reference (thường dùng với React.memo) const handleSubmit = useCallback((formData) => { onSubmit(formData); }, [onSubmit]); // Chỉ tạo function mới khi onSubmit thay đổi

Câu trả lời gây ấn tượng: không phải lúc nào cũng nên dùng useCallback/useMemo — chúng có overhead của chính mình (lưu cache, so sánh dependency). Chỉ dùng khi: (1) tính toán thực sự nặng, (2) function truyền vào React.memo component, (3) là dependency của useEffect khác.

2.4 useRef — Hai use case quan trọng

// Use case 1: Truy cập DOM element trực tiếp const inputRef = useRef(null); const focusInput = () => inputRef.current.focus(); return ;

// Use case 2: Lưu giá trị không trigger re-render const renderCount = useRef(0); useEffect(() => { renderCount.current += 1; // Tăng counter nhưng không re-render });

// Khác với useState: thay đổi ref.current KHÔNG trigger re-render

2.5 Custom Hooks — Điểm cộng lớn trong phỏng vấn

// Custom hook: tách logic khỏi component function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null);

useEffect(() => { let cancelled = false; // Tránh race condition

setLoading(true);
fetch(url)
  .then(res => res.json())
  .then(data => { if (!cancelled) setData(data); })
  .catch(err => { if (!cancelled) setError(err); })
  .finally(() => { if (!cancelled) setLoading(false); });

return () => { cancelled = true; }; // Cleanup: cancel nếu URL thay đổi

}, [url]);

return { data, loading, error }; }

// Dùng trong component function UserProfile({ userId }) { const { data: user, loading, error } = useFetch(`/api/users/${userId}`); if (loading) return ; if (error) return ; return {user.name}; }

3. State, Props và Component Design

3.1 State lifting — Khi nào và cách làm đúng

Khi hai component anh em cần share state, giải pháp là “lift state up” — đưa state lên component cha chung gần nhất, rồi truyền xuống qua props. Đây là câu hỏi kiểm tra hiểu biết về data flow một chiều (unidirectional data flow) trong React.

// Trước: state nằm trong ChildA, ChildB không đọc được // Sau khi lift state lên Parent: function Parent() { const [selectedId, setSelectedId] = useState(null); return ( <> ); }

3.2 Controlled vs Uncontrolled Components

Controlled: React hoàn toàn kiểm soát giá trị của form element qua state. Mọi thay đổi phải qua setState — single source of truth.

// Controlled — React kiểm soát value const [email, setEmail] = useState(”); return setEmail(e.target.value)} />;

Uncontrolled: DOM tự quản lý giá trị, React đọc khi cần qua ref. Ít code hơn nhưng khó validate real-time và tích hợp với state phức tạp.

// Uncontrolled — DOM tự quản lý, đọc khi submit const emailRef = useRef(); const handleSubmit = () => console.log(emailRef.current.value); return ;

3.3 React.memo — Tránh re-render không cần thiết

// Không có React.memo: re-render mỗi khi Parent render, dù props không đổi const ExpensiveChild = ({ data }) => {data};

// Có React.memo: chỉ re-render khi props thực sự thay đổi (shallow compare) const ExpensiveChild = React.memo(({ data }) => {data});

// Lưu ý: React.memo dùng shallow comparison — object/array mới tạo mỗi render // vẫn trigger re-render dù giá trị không đổi → cần useCallback/useMemo kết hợp

4. Performance Optimization — Câu hỏi phân biệt junior và mid-level

4.1 Code Splitting với React.lazy và Suspense

// Không có code splitting: toàn bộ bundle tải một lần → load time chậm // Có code splitting: tải chunk theo nhu cầu

const Dashboard = React.lazy(() => import(’./Dashboard’)); const Analytics = React.lazy(() => import(’./Analytics’));

function App() { return ( ); } // Dashboard.js và Analytics.js được tải riêng khi user navigate đến route đó

4.2 List Virtualization — Render danh sách lớn

Render 10.000 item trong DOM cùng lúc là thảm họa performance. Giải pháp: virtualization — chỉ render những item đang visible trong viewport. Thư viện phổ biến: react-window hoặc react-virtual.

import { FixedSizeList } from ‘react-window’;

// Chỉ render ~20 row visible thay vì 10.000 row

{({ index, style }) => (Row {index}: {data[index].name})}

4.3 Tránh Prop Drilling với Context API

// Tạo context const ThemeContext = React.createContext(‘light’);

// Provider ở component cha function App() { const [theme, setTheme] = useState(‘light’); return (

   {/\* Không cần truyền theme qua props nữa \*/}

); }

// Consume ở bất kỳ component con nào, dù sâu đến đâu function Button() { const { theme } = useContext(ThemeContext); return ; }

Lưu ý quan trọng hay bị hỏi: Context không phải state management — mỗi khi context value thay đổi, tất cả component dùng useContext đó đều re-render. Với state phức tạp và update thường xuyên, dùng Zustand hoặc Redux Toolkit thay vì Context.

5. Ecosystem — React Router và State Management

5.1 React Router v6 — Những thay đổi cần biết

// React Router v6 — cú pháp mới import { BrowserRouter, Routes, Route, useNavigate, useParams } from ‘react-router-dom’;

function App() { return ( ); }

function UserDetail() { const { id } = useParams(); // Lấy route param const navigate = useNavigate(); // Thay useHistory return ; }

5.2 Khi nào dùng Redux, khi nào Context là đủ?

Đây là câu hỏi interviewer dùng để test tư duy kiến trúc. Câu trả lời theo framework:

Redux Toolkit (RTK) là cách viết Redux hiện đại — giảm boilerplate đáng kể so với Redux vanilla. RTK Query còn tích hợp sẵn data fetching và caching, cạnh tranh trực tiếp với React Query.

Để chuẩn bị phỏng vấn kỹ thuật toàn diện hơn, xem thêm bài Câu hỏi phỏng vấn Backend Junior để nắm phần authentication và API design thường xuất hiện song song với React trong các buổi phỏng vấn Fullstack.

6. 6 sai lầm phổ biến khi trả lời phỏng vấn ReactJS

  1. Giải thích Virtual DOM sai — nói nó “nhanh hơn DOM thực” → Fix: Virtual DOM không nhanh hơn DOM thực về mặt tuyệt đối. Nó nhanh hơn vì giảm số lần thao tác DOM thực không cần thiết. Một thao tác DOM thực trực tiếp vẫn nhanh hơn qua Virtual DOM.
  2. Không biết giải thích tại sao cần cleanup trong useEffect → Fix: nắm rõ memory leak và stale closure — đây là lý do thực tế quan trọng hơn nhiều so với việc thuộc syntax.
  3. Dùng useCallback/useMemo mọi nơi vì nghĩ “tối ưu là luôn tốt” → Fix: memoization có chi phí — lưu cache, so sánh dependency. Chỉ dùng khi có profiling cho thấy vấn đề thực sự.
  4. Nói key trong list chỉ để “React không warning” → Fix: key là cơ chế giúp React identify element nào là cùng một item trong reconciliation. Hiểu sai dẫn đến dùng index làm key — một anti-pattern phổ biến.
  5. Không phân biệt được controlled và uncontrolled component → Fix: đây là câu hỏi cơ bản nhưng nhiều junior bỏ qua. Nắm rõ trade-off của từng cái để chọn đúng theo context.
  6. Không có ý kiến về khi nào dùng Redux vs Context → Fix: interviewer hỏi câu này để test tư duy — không có câu trả lời tuyệt đối đúng, nhưng phải có framework để quyết định. Trả lời “tùy project” mà không giải thích là điểm trừ lớn.

7. FAQ — Câu hỏi thường gặp nhất khi phỏng vấn ReactJS

7.1 React 18 có gì mới cần biết khi phỏng vấn?

Ba điểm quan trọng nhất: (1) Automatic Batching — React 18 tự động batch nhiều setState calls trong async functions, giảm số lần re-render. Trước React 18, chỉ batch trong event handlers. (2) Concurrent Features — useTransition và useDeferredValue cho phép mark một số update là “non-urgent”, giữ UI responsive trong khi xử lý heavy update. (3) Suspense trên server — hỗ trợ streaming SSR với React Server Components.

7.2 useEffect vs useLayoutEffect — khi nào dùng cái nào?

useEffect chạy bất đồng bộ sau khi browser đã paint — phù hợp cho data fetching, subscription, logging. useLayoutEffect chạy đồng bộ sau DOM mutation nhưng trước khi browser paint — phù hợp khi cần đọc layout từ DOM (ví dụ: tính toán vị trí tooltip, scroll position) để tránh flicker. Trong hầu hết trường hợp, useEffect là đủ và được khuyến nghị. useLayoutEffect chỉ dùng khi thấy visual flicker với useEffect.

7.3 Error Boundary là gì và implement thế nào?

Error Boundary là class component bắt JavaScript error trong cây component con, log lỗi, và hiển thị fallback UI thay vì crash toàn bộ app. Phải là class component vì cần lifecycle getDerivedStateFromError và componentDidCatch — chưa có hook tương đương. Thư viện react-error-boundary cung cấp wrapper tiện lợi hơn. Đặc biệt quan trọng trong production để tránh blank screen khi có lỗi bất ngờ.

7.4 Tại sao không gọi hooks trong điều kiện hay vòng lặp?

React theo dõi hooks theo thứ tự gọi — không phải theo tên. Mỗi lần render, React expect hooks được gọi theo đúng thứ tự như lần trước. Nếu gọi hook trong if/else hay loop, thứ tự có thể thay đổi giữa các lần render → React ghép nhầm state vào hook sai → bug khó tìm và khó debug. Đây là lý do tồn tại “Rules of Hooks”.

7.5 Forwardref dùng để làm gì?

Bình thường, ref không được truyền qua props như prop thông thường — React đặc biệt xử lý ref. forwardRef cho phép component nhận ref từ cha và forward xuống DOM element bên trong. Dùng phổ biến khi build component library (ví dụ: custom Input component cần expose ref để cha có thể focus/blur). React 19 đã simplify bằng cách cho phép truyền ref như prop thông thường — nhưng forwardRef vẫn cần biết cho codebase React 18 trở xuống.

Tổng kết và bước tiếp theo

Để vượt qua phỏng vấn ReactJS junior, bạn cần nắm bốn trụ cột: hiểu bản chất Virtual DOM và reconciliation (không chỉ thuộc định nghĩa), hooks thực sự hoạt động thế nào (đặc biệt useEffect cleanup và stale closure), khi nào tối ưu performance (không phải mọi lúc), và tư duy kiến trúc về state management. Cách chuẩn bị hiệu quả nhất: build một project nhỏ thực tế có đủ các pattern này — fetch data với custom hook, form với controlled component, list dài với virtualization, shared state với Context. Không gì thay thế được kinh nghiệm tự tay làm và debug. Đọc thêm bài Tips phỏng vấn Junior IT: A-Z từ chuẩn bị đến offer để chuẩn bị toàn diện hơn cho buổi phỏng vấn sắp tới.


Chia sẻ bài viết:

Bài viết liên quan

Mức lương fresher junior IT 2026 - PHP, NodeJS, React, Flutter thực tế

Bảng lương thực tế của fresher và junior IT Việt Nam năm 2026 theo từng stack - PHP/Laravel, Node.js, ReactJS, Flutter - kèm cách đọc con số để biết bạn đang ở đâu trên thị trường, dù bạn chưa đi làm hay đã đi làm 1-2 năm.

Portfolio cho dev junior - cần có gì để được chú ý

Portfolio developer junior là tập hợp các project thực tế, kỹ năng và thông tin nghề nghiệp mà bạn trình bày để nhà tuyển dụng đánh giá năng lực - thay thế cho phần kinh nghiệm làm việc còn trống trên CV. Với fresher và junior developer, portfolio không phải thứ "có thì tốt" - đó là bằng chứng duy nhất bạn có thể đưa ra để chứng minh bạn thực sự biết làm việc, không chỉ biết lý thuyết.

Cover Letter Fresher IT: Cách Viết Để Được Gọi Phỏng Vấn

Cách viết cover letter cho fresher IT khi chưa có kinh nghiệm, kèm mẫu thực tế, cấu trúc chuẩn và mẹo tăng tỷ lệ vượt qua vòng lọc CV.

Ngôn ngữ cơ thể trong phỏng vấn — những điều vô tình mất điểm

Ngôn ngữ cơ thể phỏng vấn IT là tập hợp những tín hiệu phi ngôn ngữ — tư thế, ánh mắt, cử chỉ tay, giọng điệu — mà nhà tuyển dụng quan sát song song với câu trả lời kỹ thuật của bạn. Hiểu và kiểm soát tốt những tín hiệu này giúp bạn tạo ấn tượng chuyên nghiệp ngay từ những giây đầu tiên bước vào phòng phỏng vấn.

Cách đặt câu hỏi ngược khi phỏng vấn IT — đừng chỉ hỏi lương

Hướng dẫn cách đặt câu hỏi ngược khi phỏng vấn IT dành cho fresher và junior developer — tại sao phần "bạn có câu hỏi gì không" quan trọng hơn bạn nghĩ, 20+ câu hỏi thực tế phân loại theo mục đích, những câu tuyệt đối không nên hỏi, và cách chọn đúng câu hỏi theo từng vòng phỏng vấn.

Vì sao bạn chọn ngành IT — câu trả lời tạo ấn tượng với HR khi phỏng vấn

Hướng dẫn cách trả lời câu hỏi "vì sao bạn chọn ngành IT" trong phỏng vấn — phân tích điều HR thực sự muốn nghe, framework xây dựng câu trả lời theo từng hoàn cảnh, script mẫu cho fresher và career changer, cùng các lỗi phổ biến khiến câu trả lời nghe sáo rỗng và không đáng tin.

Điểm yếu lớn nhất của bạn là gì — cách trả lời không bị loại ngay khi phỏng vấn IT

Hướng dẫn cách trả lời câu hỏi "điểm yếu lớn nhất của bạn là gì" trong phỏng vấn IT — phân tích tại sao câu này là bẫy, framework trả lời 3 bước, script mẫu cho fresher và junior developer, cùng danh sách lỗi hay gặp khiến ứng viên bị loại ngay lập tức.

Refresh Token Node.js và Laravel: Hướng Dẫn Implement Chuẩn Production 2026

Hướng dẫn implement Refresh Token hoàn chỉnh cho cả Node.js (Express) và Laravel PHP — từ thiết kế database, viết API endpoint, xử lý rotation, đến các lỗi thường gặp — giúp developer xây hệ thống JWT authentication chuẩn production trong năm 2026.

Session vs JWT: Developer Nên Chọn Cái Nào? So Sánh Thực Tế 2026

Phân tích chi tiết Session và JWT theo 6 tiêu chí kỹ thuật thực tế - kiến trúc, revocation, scaling, hiệu năng, bảo mật và độ phức tạp — giúp developer đưa ra quyết định đúng cho từng loại dự án trong năm 2026.

JWT Security Best Practices: Bảo mật JWT đúng cách trong dự án thực tế 2026

Bạn đã biết JWT là gì và dùng được trong dự án, nhưng liệu bạn có đang mắc những lỗi bảo mật mà hacker chỉ cần vài phút để khai thác? Nhiều developer triển khai JWT đúng về mặt chức năng nhưng sai hoàn toàn về bảo mật — lưu token trong localStorage, dùng secret key yếu, không rotate refresh token, hay bỏ qua validate claims.


Bài trước
Chứng Chỉ NVSP: Giải Pháp Việc Làm Thông Minh Cho Sinh Viên IT Thời AI 2026
Bài tiếp theo
Refresh Token Node.js và Laravel: Hướng Dẫn Implement Chuẩn Production 2026