REACT INTERVIEW QUESTIONS - 3 YEARS EXPERIENCE
1. ADVANCED HOOKS
Q: Explain useReducer and when to use it over useState
A:
useReducer is useful for complex state logic that involves multiple sub-values or
when the next state depends on the previous one.
Example:
```jsx
import React, { useReducer } from 'react';
const todoReducer = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, {
id: Date.now(),
text: action.payload,
completed: false
}]
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
};
default:
return state;
}
};
function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, { todos: [] });
const addTodo = (text) => {
dispatch({ type: 'ADD_TODO', payload: text });
};
const toggleTodo = (id) => {
dispatch({ type: 'TOGGLE_TODO', payload: id });
};
return (
<div>
<button onClick={() => addTodo('New todo')}>Add Todo</button>
{state.todos.map(todo => (
<div key={todo.id} onClick={() => toggleTodo(todo.id)}>
{todo.text} - {todo.completed ? 'Done' : 'Pending'}
</div>
))}
</div>
);
}
```
Q: Create custom hooks for common functionality
A:
```jsx
// Custom hook for API calls
function useApi(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
// Custom hook for localStorage
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = (value) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
```
2. CONTEXT API AND STATE MANAGEMENT
Q: Implement a theme context with React Context API
A:
```jsx
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
const themeStyles = {
light: { backgroundColor: '#ffffff', color: '#333333' },
dark: { backgroundColor: '#333333', color: '#ffffff' }
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme, styles:
themeStyles[theme] }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
function Header() {
const { theme, toggleTheme, styles } = useTheme();
return (
<header style={styles}>
<h1>My App</h1>
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'Dark' : 'Light'} Mode
</button>
</header>
);
}
```
3. PERFORMANCE OPTIMIZATION
Q: Explain React.memo, useMemo, and useCallback
A:
```jsx
import React, { useState, useMemo, useCallback, memo } from 'react';
// Memoized component
const ExpensiveComponent = memo(({ data, onItemClick }) => {
console.log('ExpensiveComponent rendered');
return (
<div>
{data.map(item => (
<div key={item.id} onClick={() => onItemClick(item.id)}>
{item.name} - {item.value}
</div>
))}
</div>
);
});
function PerformanceApp() {
const [users, setUsers] = useState([
{ id: 1, name: 'John', email: 'john@example.com' },
{ id: 2, name: 'Jane', email: 'jane@example.com' }
]);
const [count, setCount] = useState(0);
// Memoized expensive calculation
const expensiveData = useMemo(() => {
console.log('Computing expensive data...');
return users.map(user => ({
id: user.id,
name: user.name,
value: user.name.length * user.email.length
}));
}, [users]);
// Memoized callback
const handleItemClick = useCallback((itemId) => {
console.log('Item clicked:', itemId);
}, []);
return (
<div>
<button onClick={() => setCount(prev => prev + 1)}>
Count: {count}
</button>
<ExpensiveComponent
data={expensiveData}
onItemClick={handleItemClick}
/>
</div>
);
}
```
4. ADVANCED PATTERNS
Q: Implement Render Props pattern
A:
```jsx
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return render({ data, loading, error });
}
// Usage
<DataFetcher
url="/api/users"
render={({ data, loading, error }) => {
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!data) return <div>No data</div>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}}
/>
```
5. ERROR BOUNDARIES
Q: Implement an error boundary with retry functionality
A:
```jsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
retryCount: 0
};
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
}
handleRetry = () => {
this.setState(prev => ({
hasError: false,
error: null,
retryCount: prev.retryCount + 1
}));
};
render() {
if (this.state.hasError) {
return (
<div>
<h2>Something went wrong!</h2>
<p>We're sorry, but something unexpected happened.</p>
{this.state.retryCount < 3 && (
<button onClick={this.handleRetry}>
Try Again ({this.state.retryCount + 1}/3)
</button>
)}
<button onClick={() => window.location.reload()}>
Reload Page
</button>
</div>
);
}
return this.props.children;
}
}
```
6. TESTING REACT COMPONENTS
Q: How do you test React components?
A:
```jsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
// Component to test
function LoginForm({ onSubmit, loading = false }) {
const [formData, setFormData] = useState({ email: '', password: '' });
const [errors, setErrors] = useState({});
const handleSubmit = (e) => {
e.preventDefault();
const newErrors = {};
if (!formData.email) newErrors.email = 'Email is required';
if (!formData.password) newErrors.password = 'Password is required';
if (Object.keys(newErrors).length === 0) {
onSubmit(formData);
} else {
setErrors(newErrors);
}
};
return (
<form onSubmit={handleSubmit} data-testid="login-form">
<input
name="email"
value={formData.email}
onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
data-testid="email-input"
/>
{errors.email && <span data-testid="email-error">{errors.email}</span>}
<input
name="password"
type="password"
value={formData.password}
onChange={(e) => setFormData(prev => ({ ...prev, password:
e.target.value }))}
data-testid="password-input"
/>
{errors.password && <span
data-testid="password-error">{errors.password}</span>}
<button type="submit" disabled={loading} data-testid="submit-button">
{loading ? 'Logging in...' : 'Login'}
</button>
</form>
);
}
// Test
describe('LoginForm', () => {
const mockOnSubmit = jest.fn();
test('shows validation errors for empty fields', async () => {
render(<LoginForm onSubmit={mockOnSubmit} />);
const submitButton = screen.getByTestId('submit-button');
fireEvent.click(submitButton);
await waitFor(() => {
expect(screen.getByTestId('email-error')).toHaveTextContent('Email is
required');
expect(screen.getByTestId('password-error')).toHaveTextContent('Password is
required');
});
});
test('submits form with valid data', async () => {
render(<LoginForm onSubmit={mockOnSubmit} />);
fireEvent.change(screen.getByTestId('email-input'), {
target: { value: 'test@example.com' }
});
fireEvent.change(screen.getByTestId('password-input'), {
target: { value: 'password123' }
});
fireEvent.click(screen.getByTestId('submit-button'));
await waitFor(() => {
expect(mockOnSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123'
});
});
});
});
```
7. PRACTICAL CODING EXERCISES
Q: Create a reusable Modal component with portal
A:
```jsx
import React, { useEffect } from 'react';
import { createPortal } from 'react-dom';
function Modal({ isOpen, onClose, title, children }) {
useEffect(() => {
const handleEscape = (e) => {
if (e.key === 'Escape') {
onClose();
}
};
if (isOpen) {
document.addEventListener('keydown', handleEscape);
document.body.style.overflow = 'hidden';
}
return () => {
document.removeEventListener('keydown', handleEscape);
document.body.style.overflow = 'unset';
};
}, [isOpen, onClose]);
if (!isOpen) return null;
return createPortal(
<div style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 1000
}}>
<div style={{
backgroundColor: 'white',
padding: '2rem',
borderRadius: '8px',
maxWidth: '500px',
width: '90%'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems:
'center' }}>
<h2>{title}</h2>
<button onClick={onClose}>×</button>
</div>
{children}
</div>
</div>,
document.body
);
}
// Usage
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
<Modal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
title="Confirmation"
>
<p>Are you sure you want to proceed?</p>
<button onClick={() => setIsModalOpen(false)}>Cancel</button>
<button onClick={() => {
console.log('Confirmed');
setIsModalOpen(false);
}}>Confirm</button>
</Modal>
</div>
);
}
```
Q: Implement infinite scrolling with React
A:
```jsx
import React, { useState, useEffect, useRef, useCallback } from 'react';
function InfiniteScroll({ fetchData, renderItem }) {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [page, setPage] = useState(1);
const observer = useRef();
const lastItemRef = useCallback(node => {
if (loading) return;
if (observer.current) observer.current.disconnect();
observer.current = new IntersectionObserver(entries => {
if (entries[0].isIntersecting && hasMore) {
setPage(prevPage => prevPage + 1);
}
});
if (node) observer.current.observe(node);
}, [loading, hasMore]);
useEffect(() => {
const loadData = async () => {
setLoading(true);
try {
const newData = await fetchData(page);
if (newData.length === 0) {
setHasMore(false);
} else {
setItems(prev => [...prev, ...newData]);
}
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
};
loadData();
}, [page, fetchData]);
return (
<div>
{items.map((item, index) => {
if (items.length === index + 1) {
return (
<div key={item.id} ref={lastItemRef}>
{renderItem(item)}
</div>
);
} else {
return (
<div key={item.id}>
{renderItem(item)}
</div>
);
}
})}
{loading && <div>Loading...</div>}
{!hasMore && <div>No more items</div>}
</div>
);
}
// Usage
function App() {
const fetchData = async (page) => {
// Simulate API call
const response = await fetch(`/api/items?page=${page}&limit=10`);
return response.json();
};
const renderItem = (item) => (
<div style={{ padding: '1rem', border: '1px solid #ccc', margin: '0.5rem' }}>
<h3>{item.title}</h3>
<p>{item.description}</p>
</div>
);
return (
<InfiniteScroll fetchData={fetchData} renderItem={renderItem} />
);
}
```
COMMON INTERVIEW TIPS FOR 3 YEARS EXPERIENCE:
- Understand advanced hooks (useReducer, useMemo, useCallback)
- Know how to create and use custom hooks
- Be comfortable with Context API and state management
- Understand performance optimization techniques
- Know testing strategies and tools
- Be able to implement design patterns
- Understand error boundaries and error handling
- Know how to optimize component re-renders
- Be familiar with code splitting and lazy loading
- Understand the difference between controlled and uncontrolled components