# React面接でよく聞かれる質問トップ30:合格のための完全ガイド
> 2026年に最も頻出するReact面接質問30選。詳細な回答とコード例で、Reactエンジニア職の合格を目指すための実践的な準備ガイドです。
- Published: 2026-01-31
- Updated: 2026-04-21
- Author: SharpSkill
- Tags: react interview, frontend interview, react questions, javascript, technical interview
- Reading time: 18 min
---
Reactの技術面接では、基礎概念、応用パターン、ベストプラクティスへの理解が問われます。本ガイドでは、最も頻繁に出題される30の質問を取り上げ、詳細な回答とコード例を通じて効果的な準備を支援します。
> **準備のためのアドバイス**
>
> これらの質問は難易度順に整理されています。基礎を固めてから応用概念に進むことで、より体系的に準備を進めることができます。
## Reactの基礎
### 1. Virtual DOMとは何か、なぜReactは使うのか
Virtual DOMは、実際のDOMを軽量なJavaScriptで表現したものです。Reactはこの抽象化を利用して、インターフェースの更新を最適化します。
処理は3つのステップで進みます。Reactはまず仮想的なDOMのコピーを作成し、変更が発生するとそのコピーを以前のバージョンと比較し(diffingアルゴリズム)、最後に必要な変更のみを実際のDOMに適用します(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}
)
}
```
このアプローチにより、コストの高いDOM操作を回避し、複雑なインターフェースでも高速な更新が可能になります。
### 2. 関数コンポーネントとクラスコンポーネントの違いは何か
関数コンポーネントは、propsを受け取りJSXを返すJavaScriptの関数です。React 16.8以降、フックを使うことで関数コンポーネント内でもstateやライフサイクルを扱えるようになりました。
```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
}
}
```
現在は関数コンポーネントが標準です。クラスコンポーネントは引き続きサポートされていますが、新規プロジェクトでは推奨されません。
### 3. JSXはどのように動作するのか
JSXはJavaScriptの構文拡張であり、コード内にマークアップを記述できる仕組みです。HTMLではなく、見た目を整えたJavaScriptです。
```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')
)
```
HTMLとの違いとして、`class`の代わりに`className`、`for`の代わりに`htmlFor`、属性のキャメルケース表記(`onClick`、`tabIndex`)、自己終了タグの必須化などが挙げられます。
### 4. stateとpropsの違いは何か
Propsは親コンポーネントから子コンポーネントへ渡されるデータで、読み取り専用です。Stateはコンポーネント内部の状態であり、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
```
基本ルールは、propsは上から下へ(親から子へ)流れ、stateは各コンポーネントに固有であるという点です。
### 5. リストにおけるkeyはなぜ重要なのか
KeyはReactがリスト内のどの要素が変更、追加、削除されたかを識別するために使われます。一意で安定したkeyがない場合、Reactは予期しない挙動を示すことがあります。
```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. useStateとよくある落とし穴を説明する
`useState`は関数コンポーネント内でローカルstateを管理するためのフックです。setterは値そのものまたは更新関数を受け取れます。
```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は依存配列とともにどのように動作するのか
`useEffect`はレンダリング後にサイドエフェクトを実行します。依存配列によって、エフェクトがいつ再実行されるかが制御されます。
```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)
}, [])
```
> **ESLintルール**
>
> 依存配列の漏れを検出するため、`eslint-plugin-react-hooks`を必ず有効にしておくことが推奨されます。このルールは、診断が難しい多くのバグを未然に防ぎます。
### 8. useMemoとuseCallbackはいつ使うべきか
これらのフックはメモ化を行い、不要な再計算や関数の再生成を回避します。ただし、過剰な使用には注意が必要です。
```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}
}
```
これらのフックは、明確なパフォーマンス問題がある場合や、メモ化されたコンポーネントへ渡す参照を安定化させたい場合にのみ使用するべきです。
### 9. useRefはどのように動作し、どのような用途で使うのか
`useRef`はレンダリング間で保持される変更可能な参照を作成します。値が変わっても再レンダリングは発生しません。
```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. useContextを説明し、いつ使うべきか
`useContext`はprop drillingを避けてReactのコンテキストにアクセスするためのフックです。テーマやログイン中のユーザーなど、グローバルなデータに適しています。
```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 (
)
}
```
## 応用パターン
### 11. Higher-Order Component (HOC)とは何か
HOCはコンポーネントを受け取り、強化された新しいコンポーネントを返す関数です。フックの登場以来使用頻度は減りましたが、一部のライブラリでは引き続き利用されています。
```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. Render Propsパターンを説明する
Render Propsパターンは、関数であるpropを介してコンポーネント間でロジックを共有する手法です。
```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