# Top 30 câu hỏi phỏng vấn React: Hướng dẫn toàn diện để thành công
> 30 câu hỏi phỏng vấn React được hỏi nhiều nhất năm 2026. Câu trả lời chi tiết, ví dụ mã và lời khuyên để có được vị trí React developer.
- Published: 2026-01-31
- Updated: 2026-04-21
- Author: SharpSkill
- Tags: react interview, frontend interview, react questions, javascript, technical interview
- Reading time: 18 min
---
Các buổi phỏng vấn kỹ thuật React đánh giá mức độ hiểu biết về khái niệm nền tảng, pattern nâng cao và các thực hành tốt nhất. Hướng dẫn này tổng hợp 30 câu hỏi xuất hiện thường xuyên nhất kèm câu trả lời chi tiết và ví dụ mã, giúp việc chuẩn bị trở nên hiệu quả.
> **Lời khuyên chuẩn bị**
>
> Các câu hỏi được sắp xếp theo mức độ khó. Việc nắm vững các khái niệm nền tảng trước khi tiến đến các chủ đề nâng cao giúp quá trình ôn luyện có cấu trúc hơn.
## Nền tảng React
### 1. Virtual DOM là gì và vì sao React sử dụng nó?
Virtual DOM là một biểu diễn nhẹ bằng JavaScript của DOM thật. React dùng lớp trừu tượng này để tối ưu hóa việc cập nhật giao diện.
Quy trình gồm ba bước: trước tiên React tạo một bản sao ảo của DOM, sau đó so sánh bản sao này với phiên bản trước khi có thay đổi (thuật toán diffing) và cuối cùng chỉ áp dụng những thay đổi cần thiết lên DOM thật (reconciliation).
```jsx
// Simplified example of the concept
// When state changes, React doesn't recreate the entire DOM
function Counter() {
const [count, setCount] = useState(0)
// Only the span containing count will be updated in the real DOM
// The rest of the component is untouched
return (
Counter
{count}
)
}
```
Cách tiếp cận này tránh được các thao tác tốn kém trên DOM và cho phép cập nhật mượt mà ngay cả với giao diện phức tạp.
### 2. Sự khác nhau giữa component hàm và component lớp là gì?
Component hàm là các hàm JavaScript nhận props và trả về JSX. Từ React 16.8, hooks cho phép dùng state và vòng đời ngay trong component hàm.
```jsx
// Functional component (recommended)
// More concise, easier to test, supports hooks
function Welcome({ name }) {
const [visits, setVisits] = useState(0)
useEffect(() => {
setVisits(v => v + 1)
}, [])
return
Hello {name}, visit #{visits}
}
// Class component (legacy)
// More verbose, requires this binding
class WelcomeClass extends React.Component {
state = { visits: 0 }
componentDidMount() {
this.setState(prev => ({ visits: prev.visits + 1 }))
}
render() {
return
}
}
```
Component hàm hiện là chuẩn mực. Component lớp vẫn được hỗ trợ nhưng không còn được khuyến nghị cho các dự án mới.
### 3. JSX hoạt động như thế nào?
JSX là phần mở rộng cú pháp của JavaScript cho phép viết markup trực tiếp trong mã. Đây không phải HTML mà là JavaScript được ngụy trang.
```jsx
// What we write (JSX)
const element = (
Title
Paragraph
)
// What Babel compiles (pure JavaScript)
const element = React.createElement(
'div',
{ className: 'container' },
React.createElement('h1', null, 'Title'),
React.createElement('p', null, 'Paragraph')
)
```
Các khác biệt so với HTML gồm: `className` thay cho `class`, `htmlFor` thay cho `for`, thuộc tính theo dạng camelCase (`onClick`, `tabIndex`) và yêu cầu đóng các thẻ tự đóng.
### 4. State và props khác nhau ở đâu?
Props là dữ liệu được truyền từ component cha sang component con, chỉ đọc. State là trạng thái nội bộ của component và có thể thay đổi thông qua setter.
```jsx
// UserCard.jsx
// name and role are props (immutable)
function UserCard({ name, role }) {
// isExpanded is state (mutable)
const [isExpanded, setIsExpanded] = useState(false)
return (
{name}
{role}
{/* Modifying state triggers a re-render */}
{isExpanded && }
)
}
// Usage
```
Nguyên tắc cơ bản: props chảy xuống (cha → con), còn state là cục bộ cho từng component.
### 5. Vì sao key lại quan trọng trong danh sách?
Key giúp React xác định phần tử nào đã thay đổi, được thêm hay xóa khỏi danh sách. Khi thiếu key duy nhất và ổn định, React có thể có những hành vi không lường trước.
```jsx
// ❌ Bad practice: index as key
// Problem: if order changes, React loses tracking
{items.map((item, index) => (
))}
// ✅ Good practice: unique and stable identifier
{items.map(item => (
))}
// Concrete example of the problem with indices
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React' },
{ id: 2, text: 'Create a project' }
])
// When deleting the first element with key={index}
// React will think element 0's content changed
// instead of understanding an element was removed
return (
{todos.map(todo => (
{todo.text}
))}
)
}
```
## React Hooks
### 6. Giải thích useState và những cạm bẫy thường gặp
`useState` quản lý state cục bộ trong một component hàm. Setter có thể nhận một giá trị hoặc một hàm cập nhật.
```jsx
// Declaration with initial value
const [count, setCount] = useState(0)
// ❌ Pitfall: multiple updates in the same cycle
function increment() {
setCount(count + 1) // count = 0, sets 1
setCount(count + 1) // count = 0 still, sets 1
setCount(count + 1) // count = 0 still, sets 1
// Final result: 1 (not 3)
}
// ✅ Solution: use the update function
function incrementCorrect() {
setCount(prev => prev + 1) // 0 → 1
setCount(prev => prev + 1) // 1 → 2
setCount(prev => prev + 1) // 2 → 3
// Final result: 3
}
// ❌ Pitfall: object mutation
const [user, setUser] = useState({ name: 'Alice', age: 25 })
user.age = 26 // Direct mutation, no re-render
// ✅ Solution: create a new object
setUser({ ...user, age: 26 })
// or
setUser(prev => ({ ...prev, age: 26 }))
```
### 7. useEffect hoạt động thế nào với mảng dependency?
`useEffect` thực thi các hiệu ứng phụ sau khi render. Mảng dependency quyết định thời điểm hiệu ứng được chạy.
```jsx
// Executed on every render (rare, usually avoid)
useEffect(() => {
console.log('Render completed')
})
// Executed only on mount (equivalent to componentDidMount)
useEffect(() => {
console.log('Component mounted')
// Cleanup on unmount (equivalent to componentWillUnmount)
return () => {
console.log('Component unmounted')
}
}, [])
// Executed when userId changes
useEffect(() => {
async function fetchUser() {
const response = await fetch(`/api/users/${userId}`)
const data = await response.json()
setUser(data)
}
fetchUser()
}, [userId])
// ❌ Missing dependency - subtle bug
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1) // count is "captured" at its initial value
}, 1000)
return () => clearInterval(timer)
}, []) // count is missing from dependencies
// ✅ Fix with update function
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1) // No need for count in deps
}, 1000)
return () => clearInterval(timer)
}, [])
```
> **Quy tắc ESLint**
>
> Nên luôn bật `eslint-plugin-react-hooks` để phát hiện các dependency bị thiếu. Quy tắc này ngăn chặn nhiều lỗi khó chẩn đoán.
### 8. Khi nào nên dùng useMemo và useCallback?
Hai hook này cho phép ghi nhớ (memoization) để tránh tính toán lại hoặc tạo lại không cần thiết. Tuy nhiên, không nên lạm dụng.
```jsx
// useMemo: memoizes a computed value
function ProductList({ products, filter }) {
// Recalculated only if products or filter change
const filteredProducts = useMemo(() => {
console.log('Filtering...')
return products.filter(p => p.category === filter)
}, [products, filter])
return
{filteredProducts.map(p =>
{p.name}
)}
}
// useCallback: memoizes a function
function ParentComponent() {
const [count, setCount] = useState(0)
// Without useCallback, handleClick is recreated on every render
// Causing unnecessary re-renders of ExpensiveChild
const handleClick = useCallback((id) => {
console.log('Clicked:', id)
}, []) // Empty deps = stable function
return (
<>
{count}
{/* React.memo on ExpensiveChild for this to be effective */}
>
)
}
// ❌ Over-optimization: not needed here
const SimpleComponent = () => {
// This calculation is trivial, useMemo adds overhead
const doubled = useMemo(() => 2 * 2, [])
return {doubled}
}
```
Chỉ nên dùng các hook này khi có vấn đề hiệu năng đã được xác định, hoặc để ổn định tham chiếu được truyền vào các component đã được memo.
### 9. useRef hoạt động ra sao và dùng trong những trường hợp nào?
`useRef` tạo ra một tham chiếu có thể thay đổi, tồn tại qua các lần render mà không kích hoạt render lại khi giá trị thay đổi.
```jsx
// Case 1: Access a DOM element
function TextInput() {
const inputRef = useRef(null)
const focusInput = () => {
inputRef.current.focus()
}
return (
<>
>
)
}
// Case 2: Store a mutable value without re-render
function Timer() {
const [seconds, setSeconds] = useState(0)
const intervalRef = useRef(null)
const start = () => {
// Store the interval ID to be able to stop it
intervalRef.current = setInterval(() => {
setSeconds(s => s + 1)
}, 1000)
}
const stop = () => {
clearInterval(intervalRef.current)
}
return (
{seconds}s
)
}
// Case 3: Keep the previous value
function usePrevious(value) {
const ref = useRef()
useEffect(() => {
ref.current = value
}, [value])
return ref.current
}
```
### 10. Giải thích useContext và khi nào nên dùng
`useContext` truy cập vào một React context mà không phải truyền props qua nhiều cấp. Phù hợp cho dữ liệu toàn cục như theme hoặc người dùng đang đăng nhập.
```jsx
// 1. Create the context
const ThemeContext = createContext({
theme: 'light',
toggleTheme: () => {}
})
// 2. Create the provider
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light')
const toggleTheme = useCallback(() => {
setTheme(prev => prev === 'light' ? 'dark' : 'light')
}, [])
// Memoize the value to avoid unnecessary re-renders
const value = useMemo(() => ({ theme, toggleTheme }), [theme, toggleTheme])
return (
{children}
)
}
// 3. Use the context
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext)
return (
)
}
// 4. Wrap the application
function App() {
return (
)
}
```
## Các pattern nâng cao
### 11. Higher-Order Component (HOC) là gì?
HOC là một hàm nhận vào một component và trả về một component mới đã được mở rộng. Ít dùng hơn kể từ khi có hooks nhưng vẫn xuất hiện trong một số thư viện.
```jsx
// HOC that adds logging
function withLogging(WrappedComponent) {
return function WithLogging(props) {
useEffect(() => {
console.log(`${WrappedComponent.name} mounted with props:`, props)
return () => {
console.log(`${WrappedComponent.name} unmounted`)
}
}, [])
return
}
}
// HOC that handles authentication
function withAuth(WrappedComponent) {
return function WithAuth(props) {
const { user, isLoading } = useAuth()
if (isLoading) return
if (!user) return
return
}
}
// Usage
const ProtectedDashboard = withAuth(Dashboard)
const LoggedButton = withLogging(Button)
```
### 12. Giải thích pattern Render Props
Pattern Render Props chia sẻ logic giữa các component thông qua một prop là một hàm.
```jsx
// Component with render prop
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 })
useEffect(() => {
const handleMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY })
}
window.addEventListener('mousemove', handleMove)
return () => window.removeEventListener('mousemove', handleMove)
}, [])
// Call the render function with data
return render(position)
}
// Usage
function App() {
return (
(
Position: {x}, {y}
)}
/>
)
}
// Modern version with custom hook (preferred)
function useMousePosition() {
const [position, setPosition] = useState({ x: 0, y: 0 })
useEffect(() => {
const handleMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY })
}
window.addEventListener('mousemove', handleMove)
return () => window.removeEventListener('mousemove', handleMove)
}, [])
return position
}
function App() {
const { x, y } = useMousePosition()
return
Position: {x}, {y}
}
```
### 13. Cách tạo một custom hook?
Custom hook dùng để tách và tái sử dụng logic có state giữa các component.
```jsx
// useLocalStorage.js
function useLocalStorage(key, initialValue) {
// Initialize with localStorage value or default
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch (error) {
console.error(error)
return initialValue
}
})
// Setter wrapper that syncs with localStorage
const setValue = useCallback((value) => {
try {
// Support update functions
const valueToStore = value instanceof Function ? value(storedValue) : value
setStoredValue(valueToStore)
window.localStorage.setItem(key, JSON.stringify(valueToStore))
} catch (error) {
console.error(error)
}
}, [key, storedValue])
return [storedValue, setValue]
}
// Usage
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light')
const [fontSize, setFontSize] = useLocalStorage('fontSize', 16)
return (
setFontSize(Number(e.target.value))}
/>
)
}
```
### 14. Compound Components Pattern là gì?
Pattern này tạo ra các component phối hợp ngầm với nhau, giống như các thẻ `