As a technical interviewer with over 8 years of experience conducting React.js interviews, I've compiled this comprehensive guide featuring real interview scenarios. This post breaks down essential React.js questions across different experience levels, complete with detailed answers and examples.
Table of Contents
Entry Level (1-2 Years Experience)
1. Interviewer: Can you explain what React.js is and its core features?
Candidate: React.js is an open-source JavaScript library developed by Facebook for building user interfaces, particularly single-page applications. Its core features include:
Virtual DOM for optimized rendering
Component-based architecture
Unidirectional data flow
JSX syntax
Let me elaborate on each. The Virtual DOM is like a lightweight copy of the actual DOM, which React uses to optimize rendering performance. When state changes occur, React first updates the Virtual DOM, compares it with the real DOM, and then efficiently updates only the necessary parts.
For example, in a simple counter component:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
When the button is clicked, React only updates the paragraph element containing the count, not the entire component.
2. Interviewer: What's the difference between state and props in React?
Candidate: State and props serve different purposes in React components. Props (short for properties) are read-only and are passed from parent to child components, while state is managed within the component and can be updated using setState or useState.
Let me demonstrate with a practical example:
// Parent Component
function ParentComponent() {
const [message, setMessage] = useState("Hello");
return (
<ChildComponent
message={message} // This is a prop
onUpdate={() => setMessage("Hi")} // Passing callback as prop
/>
);
}
// Child Component
function ChildComponent({ message, onUpdate }) {
const [count, setCount] = useState(0); // This is state
return (
<div>
<p>{message}</p> // Using prop
<p>Count: {count}</p> // Using state
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={onUpdate}>Update Message</button>
</div>
);
}
3. Interviewer: How do you handle events in React?
Candidate: In React, events are handled using camelCase syntax and JSX allows us to pass functions as event handlers. The key differences from vanilla JavaScript event handling are:
React events are named using camelCase (onClick instead of onclick)
Event handlers are passed as JSX attributes
We need to prevent default behavior explicitly
Here's a practical example:
function FormComponent() {
const handleSubmit = (e) => {
e.preventDefault(); // Preventing default form submission
console.log('Form submitted');
};
const handleInputChange = (e) => {
console.log(e.target.value);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleInputChange}
onClick={(e) => console.log('Input clicked')}
/>
<button type="submit">Submit</button>
</form>
);
}
4. Interviewer: What is JSX and why do we use it?
Candidate: JSX (JavaScript XML) is a syntax extension for JavaScript that allows us to write HTML-like code within JavaScript. It makes the code more readable and writing templates more intuitive.
Behind the scenes, JSX is converted to regular JavaScript. For example:
// JSX code
const element = (
<div className="greeting">
<h1>Hello, {user.name}!</h1>
</div>
);
// Compiles to this JavaScript code
const element = React.createElement(
'div',
{ className: 'greeting' },
React.createElement('h1', null, 'Hello, ', user.name, '!')
);
JSX provides several benefits:
Syntax familiar to HTML developers
Compile-time error checking
Enhanced security (automatic escaping)
5. Interviewer: Can you explain the component lifecycle in React?
Candidate: In modern React with hooks, the component lifecycle can be managed using useEffect. However, it's important to understand both class and functional component lifecycles.
In class components, we have:
class ExampleComponent extends React.Component {
componentDidMount() {
console.log('Component mounted');
}
componentDidUpdate(prevProps, prevState) {
if (this.props.value !== prevProps.value) {
console.log('Value changed');
}
}
componentWillUnmount() {
console.log('Component will unmount');
}
render() {
return <div>Example</div>;
}
}
In functional components using hooks:
function ExampleComponent({ value }) {
useEffect(() => {
console.log('Component mounted');
return () => {
console.log('Component will unmount');
};
}, []); // Empty dependency array = componentDidMount
useEffect(() => {
console.log('Value changed');
}, [value]); // Runs when value changes
return <div>Example</div>;
}
6. Interviewer: What are hooks in React and why were they introduced?
Candidate: Hooks are functions that allow us to "hook into" React state and lifecycle features from functional components. They were introduced in React 16.8 to solve several problems:
Reuse stateful logic between components
Reduce complexity of components
Avoid confusion with 'this' binding
Enable state management in functional components
Here's a practical example showing multiple hooks:
function UserProfile() {
// State management
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
// Side effects
useEffect(() => {
fetchUser().then(data => {
setUser(data);
setLoading(false);
});
}, []);
// Custom hook for window width
const windowWidth = useWindowSize();
if (loading) return <div>Loading...</div>;
return (
<div>
<h1>{user.name}</h1>
<p>Window width: {windowWidth}px</p>
</div>
);
}
// Custom hook example
function useWindowSize() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}
7. Interviewer: How do you handle forms in React?
Candidate: In React, we typically handle forms using controlled components, where form data is controlled by React state. Here's a comprehensive example:
function RegistrationForm() {
const [formData, setFormData] = useState({
username: '',
email: '',
password: ''
});
const [errors, setErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
// Validation logic
const validationErrors = {};
if (!formData.username) validationErrors.username = 'Username is required';
if (!formData.email) validationErrors.email = 'Email is required';
if (Object.keys(validationErrors).length === 0) {
// Submit form
console.log('Form submitted:', formData);
} else {
setErrors(validationErrors);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
placeholder="Username"
/>
{errors.username && <span className="error">{errors.username}</span>}
</div>
<div>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
<button type="submit">Register</button>
</form>
);
}
8. Interviewer: What is the significance of keys in React lists?
Candidate: Keys in React lists help identify which items have changed, been added, or been removed. They help React optimize rendering by providing a stable identity to elements.
Here's an example showing the importance of keys:
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React' },
{ id: 2, text: 'Build project' }
]);
const addTodo = (text) => {
setTodos(prev => [...prev, { id: Date.now(), text }]);
};
return (
<ul>
{todos.map(todo => (
// Using todo.id as key instead of index
<li key={todo.id}>
{todo.text}
<button onClick={() => removeTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
);
}
Using index as keys can lead to issues:
// Bad practice
{items.map((item, index) => (
<li key={index}>{item.text}</li>
))}
// Good practice
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
Mid Level (2-4 Years Experience)
1. Interviewer: Explain React's Virtual DOM and reconciliation process in detail.
Candidate: The Virtual DOM and reconciliation process is fundamental to React's performance optimization. Let me explain the process in detail:
Virtual DOM Creation: When we render a React component, it creates a tree of JavaScript objects (Virtual DOM) representing the UI.
Diffing Algorithm: When state or props change, React creates a new Virtual DOM tree and compares it with the previous one using a diffing algorithm.
Batch Updates: React batches multiple changes and performs a single update to the real DOM.
Here's an example demonstrating this:
function ComplexList({ items }) {
const [selectedId, setSelectedId] = useState(null);
return (
<div>
{items.map(item => (
<div
key={item.id}
className={selectedId === item.id ? 'selected' : ''}
onClick={() => setSelectedId(item.id)}
>
<h3>{item.title}</h3>
<p>{item.description}</p>
</div>
))}
</div>
);
}
When selectedId
changes, React:
Creates new Virtual DOM
Compares with previous Virtual DOM
Only updates the className of affected elements
Batches these changes for a single DOM update
2. Interviewer: How do you handle state management in large React applications?
Candidate: For large React applications, state management typically involves a combination of local component state, Context API, and possibly external state management libraries. Here's my approach:
Local State: For component-specific state
Context API: For shared state across components
Redux/MobX: For complex global state management
Here's an example using Context with complex state:
// Create context
const AppContext = createContext();
// Provider component
function AppProvider({ children }) {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const [notifications, setNotifications] = useState([]);
// Complex state updates
const addNotification = (message) => {
setNotifications(prev => [...prev, { id: Date.now(), message }]);
setTimeout(() => {
setNotifications(prev => prev.filter(n => n.id !== id));
}, 3000);
};
return (
<AppContext.Provider value={{
user,
setUser,
theme,
setTheme,
notifications,
addNotification
}}>
{children}
</AppContext.Provider>
);
}
// Custom hook for using context
function useApp() {
const context = useContext(AppContext);
if (!context) {
throw new Error('useApp must be used within AppProvider');
}
return context;
}
// Usage in components
function Header() {
const { user, theme, notifications } = useApp();
return (
<header className={theme}>
<h1>Welcome, {user.name}</h1>
<NotificationBadge count={notifications.length} />
</header>
);
}
3. Interviewer: What are React error boundaries and how do you implement them?
Candidate: Error boundaries are React components that catch JavaScript errors in their child component tree and display fallback UI. They're implemented using class components with specific lifecycle methods.
Here's a comprehensive implementation:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
// Update state to show fallback UI
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// Log error to error reporting service
logErrorToService(error, errorInfo);
this.setState({ errorInfo });
}
render() {
if (this.state.hasError) {
return (
<div className="error-boundary">
<h1>Something went wrong</h1>
{process.env.NODE_ENV === 'development' && (
<details>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
)}
<button onClick={() => window.location.reload()}>
Reload Page
</button>
</div>
);
}
return this.props.children;
}
}
// Usage
function App() {
return (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
}
4. Interviewer: Explain React's performance optimization techniques you've used in production.
Candidate: I've implemented several performance optimization techniques in production React applications. Here are the key ones with examples:
- Memoization with useMemo and useCallback:
function ExpensiveComponent({ data, onItemSelect }) {
// Memoize expensive calculations
const processedData = useMemo(() => {
return data.map(item => ({
...item,
calculated: expensiveCalculation(item)
}));
}, [data]);
// Memoize callbacks
const handleSelect = useCallback((id) => {
onItemSelect(id);
}, [onItemSelect]);
return (
<List data={processedData} onSelect={handleSelect} />
);
}
onItemSelect(id); }, [onItemSelect]);
return ( ); }
- React.memo for Component Memoization:
const ListItem = React.memo(function ListItem({ item, onSelect }) {
console.log(`Rendering item ${item.id}`);
return (
<div onClick={() => onSelect(item.id)}>
{item.name}
</div>
);
});
- Code Splitting with React.lazy and Suspense:
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<HeavyComponent />
</Suspense>
);
}
- Virtual List for Large Datasets:
function VirtualList({ items }) {
const [visibleRange, setVisibleRange] = useState({ start: 0, end: 20 });
const onScroll = useCallback((e) => {
const { scrollTop, clientHeight } = e.target;
const start = Math.floor(scrollTop / 50);
const end = start + Math.ceil(clientHeight / 50);
setVisibleRange({ start, end });
}, []);
return (
<div style={{ height: '400px', overflow: 'auto' }} onScroll={onScroll}>
<div style={{ height: `${items.length * 50}px`, position: 'relative' }}>
{items.slice(visibleRange.start, visibleRange.end).map(item => (
<div key={item.id} style={{ position: 'absolute', top: item.index * 50 }}>
{item.content}
</div>
))}
</div>
</div>
);
}
5. Interviewer: How do you handle API integration and data fetching in React?
Candidate: I follow several best practices for API integration in React applications. Here's a comprehensive approach:
// Custom hook for data fetching
function useApi(endpoint, options = {}) {
const [state, setState] = useState({
data: null,
error: null,
loading: true
});
useEffect(() => {
let mounted = true;
const fetchData = async () => {
try {
const response = await fetch(endpoint, {
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (mounted) {
setState({ data, error: null, loading: false });
}
} catch (error) {
if (mounted) {
setState({ data: null, error, loading: false });
}
}
};
fetchData();
return () => {
mounted = false;
};
}, [endpoint, JSON.stringify(options)]);
return state;
}
// Usage in component
function UserProfile({ userId }) {
const { data: user, error, loading } = useApi(`/api/users/${userId}`);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
6. Interviewer: Explain your approach to testing React components.
Candidate: I use a combination of unit, integration, and end-to-end tests for React applications. Here's my testing strategy using React Testing Library:
// Component to test
function PasswordInput({ onPasswordChange }) {
const [password, setPassword] = useState('');
const [strength, setStrength] = useState('weak');
const handleChange = (e) => {
const newPassword = e.target.value;
setPassword(newPassword);
setStrength(calculateStrength(newPassword));
onPasswordChange(newPassword);
};
return (
<div>
<input
type="password"
value={password}
onChange={handleChange}
data-testid="password-input"
/>
<div data-testid="strength-indicator">{strength}</div>
</div>
);
}
// Test suite
import { render, fireEvent, screen } from '@testing-library/react';
describe('PasswordInput', () => {
test('updates password strength when input changes', () => {
const handleChange = jest.fn();
render(<PasswordInput onPasswordChange={handleChange} />);
const input = screen.getByTestId('password-input');
fireEvent.change(input, { target: { value: 'weakpass' } });
expect(screen.getByTestId('strength-indicator')).toHaveTextContent('weak');
expect(handleChange).toHaveBeenCalledWith('weakpass');
fireEvent.change(input, { target: { value: 'StrongP@ssw0rd' } });
expect(screen.getByTestId('strength-indicator')).toHaveTextContent('strong');
});
});
7. Interviewer: How do you handle routing in React applications?
Candidate: I use React Router for handling navigation in React applications. Here's a comprehensive example:
import { BrowserRouter, Routes, Route, Link, useParams, Navigate } from 'react-router-dom';
// Protected Route Component
function PrivateRoute({ children }) {
const { isAuthenticated } = useAuth();
return isAuthenticated ? children : (
<Navigate to="/login" state={{ from: location }} replace />
);
}
// Main Router Setup
function AppRouter() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/dashboard">Dashboard</Link>
<Link to="/profile">Profile</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route
path="/dashboard/*"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
/>
<Route
path="/profile/:userId"
element={
<PrivateRoute>
<UserProfile />
</PrivateRoute>
}
/>
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
8. Interviewer: What's your experience with React's Server Components?
Candidate: React Server Components are a new feature that allows components to be rendered on the server. Here's an example of how I've implemented them:
// Server Component
// Note: This file would have .server.jsx extension
async function UserProfile({ userId }) {
const user = await db.user.findById(userId);
return (
<div>
<h1>{user.name}</h1>
<ClientSideInteractions user={user} />
</div>
);
}
// Client Component
// Note: This file would have .client.jsx extension
'use client';
function ClientSideInteractions({ user }) {
const [isEditing, setIsEditing] = useState(false);
return (
<div>
{isEditing ? (
<UserEditForm user={user} />
) : (
<button onClick={() => setIsEditing(true)}>
Edit Profile
</button>
)}
</div>
);
}
Senior Level (4+ Years Experience) Continued
1. Interviewer: How would you architect a large-scale React application from scratch?
Candidate: When architecting a large-scale React application, I follow a well-structured approach. Here's my detailed strategy:
// Project Structure
src/
├── api/ # API layer
│ ├── client.js # Axios/fetch config
│ └── endpoints/
├── components/ # Shared components
│ ├── common/ # Basic UI components
│ └── features/ # Feature-specific components
├── hooks/ # Custom hooks
├── context/ # React context
├── stores/ # State management
├── utils/ # Utilities and helpers
├── types/ # TypeScript types/interfaces
└── features/ # Feature-based modules
// Example of a feature-based module
Here's an example implementation:
// api/client.js
import axios from 'axios';
const apiClient = axios.create({
baseURL: process.env.REACT_APP_API_URL,
timeout: 10000,
});
apiClient.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
export default apiClient;
// features/auth/hooks/useAuth.js
export function useAuth() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const initAuth = async () => {
try {
const token = localStorage.getItem('token');
if (token) {
const user = await apiClient.get('/auth/me');
setUser(user.data);
}
} finally {
setLoading(false);
}
};
initAuth();
}, []);
return { user, loading };
}
// components/features/AuthProvider.jsx
function AuthProvider({ children }) {
const { user, loading } = useAuth();
if (loading) {
return <AppLoader />;
}
return (
<AuthContext.Provider value={{ user }}>
{children}
</AuthContext.Provider>
);
}
2. Interviewer: Explain your approach to handling complex state management scenarios in React.
Candidate: For complex state management, I implement a combination of patterns depending on the use case. Here's a comprehensive example:
// Custom hook for complex form state management
function useFormWithValidation(initialState) {
const [values, setValues] = useState(initialState);
const [errors, setErrors] = useState({});
const [dirty, setDirty] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const validateField = useCallback((name, value) => {
// Complex validation logic
const validations = {
email: (value) => {
if (!value) return 'Email is required';
if (!/\S+@\S+\.\S+/.test(value)) return 'Invalid email format';
return null;
},
password: (value) => {
if (!value) return 'Password is required';
if (value.length < 8) return 'Password must be at least 8 characters';
if (!/[A-Z]/.test(value)) return 'Password must contain uppercase letter';
if (!/[0-9]/.test(value)) return 'Password must contain number';
return null;
}
};
return validations[name]?.(value) ?? null;
}, []);
const handleChange = useCallback((name, value) => {
setValues(prev => ({
...prev,
[name]: value
}));
setDirty(prev => ({
...prev,
[name]: true
}));
const error = validateField(name, value);
setErrors(prev => ({
...prev,
[name]: error
}));
}, [validateField]);
const handleSubmit = async (onSubmit) => {
setIsSubmitting(true);
try {
const newErrors = {};
Object.keys(values).forEach(key => {
const error = validateField(key, values[key]);
if (error) newErrors[key] = error;
});
if (Object.keys(newErrors).length === 0) {
await onSubmit(values);
} else {
setErrors(newErrors);
}
} finally {
setIsSubmitting(false);
}
};
return {
values,
errors,
dirty,
isSubmitting,
handleChange,
handleSubmit
};
}
// Usage in a complex form component
function RegistrationForm() {
const form = useFormWithValidation({
email: '',
password: '',
confirmPassword: ''
});
const onSubmit = async (values) => {
await api.register(values);
};
return (
<form onSubmit={(e) => {
e.preventDefault();
form.handleSubmit(onSubmit);
}}>
<FormField
name="email"
value={form.values.email}
onChange={form.handleChange}
error={form.errors.email}
dirty={form.dirty.email}
/>
{/* Other fields */}
</form>
);
}
3. Interviewer: How do you optimize React applications for performance at scale?
Candidate: Performance optimization in large React applications requires a multi-faceted approach. Here's how I handle it:
// 1. Code Splitting with Routes
const routes = {
dashboard: {
path: '/dashboard',
component: lazy(() => import('./pages/Dashboard')),
preload: () => import('./pages/Dashboard')
}
};
// Preload component on hover
function PreloadLink({ to, children }) {
const handleMouseEnter = () => {
routes[to].preload();
};
return (
<Link
to={routes[to].path}
onMouseEnter={handleMouseEnter}
>
{children}
</Link>
);
}
// 2. Virtualized Lists for Large Datasets
function VirtualizedTable({ data, rowHeight, visibleRows }) {
const [scrollTop, setScrollTop] = useState(0);
const containerRef = useRef(null);
const startIndex = Math.floor(scrollTop / rowHeight);
const endIndex = startIndex + visibleRows;
const visibleData = data.slice(startIndex, endIndex);
const handleScroll = useCallback((e) => {
setScrollTop(e.target.scrollTop);
}, []);
return (
<div
ref={containerRef}
style={{ height: visibleRows * rowHeight, overflow: 'auto' }}
onScroll={handleScroll}
>
<div style={{ height: data.length * rowHeight }}>
<div style={{ transform: `translateY(${startIndex * rowHeight}px)` }}>
{visibleData.map(row => (
<Row key={row.id} data={row} height={rowHeight} />
))}
</div>
</div>
</div>
);
}
// 3. Optimized Context Usage
const StateContext = createContext(null);
const DispatchContext = createContext(null);
function StateProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
);
}
4. Interviewer: How do you handle micro-frontend architecture with React?
Candidate: Micro-frontend architecture requires careful planning and implementation. Here's my approach:
// Module Federation Configuration (webpack.config.js)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'dashboard',
filename: 'remoteEntry.js',
exposes: {
'./DashboardApp': './src/DashboardApp'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
]
};
// Container Application
const DashboardApp = lazy(() => import('dashboard/DashboardApp'));
const AnalyticsApp = lazy(() => import('analytics/AnalyticsApp'));
function App() {
return (
<div>
<Header />
<Suspense fallback={<Loader />}>
<Switch>
<Route path="/dashboard">
<ErrorBoundary>
<DashboardApp />
</ErrorBoundary>
</Route>
<Route path="/analytics">
<ErrorBoundary>
<AnalyticsApp />
</ErrorBoundary>
</Route>
</Switch>
</Suspense>
</div>
);
}
// Shared Event Bus
const eventBus = {
listeners: {},
subscribe(event, callback) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(callback);
},
publish(event, data) {
if (this.listeners[event]) {
this.listeners[event].forEach(callback => callback(data));
}
}
};
5. Interviewer: Explain your approach to handling real-time data and WebSocket connections in React.
Candidate: Here's how I implement real-time functionality in React applications:
// Custom WebSocket Hook
function useWebSocket(url) {
const [socket, setSocket] = useState(null);
const [messages, setMessages] = useState([]);
const [connected, setConnected] = useState(false);
useEffect(() => {
const ws = new WebSocket(url);
ws.onopen = () => {
setConnected(true);
setSocket(ws);
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
setMessages(prev => [...prev, message]);
};
ws.onclose = () => {
setConnected(false);
};
return () => {
ws.close();
};
}, [url]);
const sendMessage = useCallback((message) => {
if (socket && connected) {
socket.send(JSON.stringify(message));
}
}, [socket, connected]);
return { messages, connected, sendMessage };
}
// Real-time Chat Component
function ChatRoom({ roomId }) {
const { messages, connected, sendMessage } = useWebSocket(
`wss://api.example.com/chat/${roomId}`
);
const [newMessage, setNewMessage] = useState('');
const handleSend = () => {
sendMessage({
type: 'message',
content: newMessage,
timestamp: Date.now()
});
setNewMessage('');
};
return (
<div>
<div className="status">
{connected ? 'Connected' : 'Connecting...'}
</div>
<div className="messages">
{messages.map(msg => (
<Message key={msg.timestamp} {...msg} />
))}
</div>
<div className="input">
<input
value={newMessage}
onChange={e => setNewMessage(e.target.value)}
/>
<button onClick={handleSend}>Send</button>
</div>
</div>
);
}
6. Interviewer: How do you handle accessibility (a11y) in React applications?
Candidate: Accessibility is crucial in modern web applications. Here's my comprehensive approach:
// Accessible Component Example
function AccessibleForm() {
const [expanded, setExpanded] = useState(false);
const formId = useId();
return (
<div role="form" aria-labelledby={`${formId}-title`}>
<h2 id={`${formId}-title`}>Contact Form</h2>
<button
aria-expanded={expanded}
aria-controls={`${formId}-content`}
onClick={() => setExpanded(!expanded)}
>
{expanded ? 'Hide' : 'Show'} Form
</button>
<div
id={`${formId}-content`}
role="region"
aria-hidden={!expanded}
style={{ display: expanded ? 'block' : 'none' }}
>
<label htmlFor={`${formId}-name`}>Name</label>
<input
id={`${formId}-name`}
type="text"
aria-required="true"
aria-describedby={`${formId}-name-error`}
/>
<span id={`${formId}-name-error`} role="alert"></span>
{/* More form fields */}
</div>
</div>
);
}
// Focus Management Hook
function useFocusTrap(ref) {
useEffect(() => {
const element = ref.current;
if (!element) return;
const focusableElements = element.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstFocusable = focusableElements[0];
const lastFocusable = focusableElements[focusableElements.length - 1];
const handleTabKey = (e) => {
if (e.key !== 'Tab') return;
if (e.shiftKey) {
if (document.activeElement === firstFocusable) {
e.preventDefault();
lastFocusable.focus();
}
} else {
if (document.activeElement === lastFocusable) {
e.preventDefault();
firstFocusable.focus();
}
}
};
element.addEventListener('keydown', handleTabKey);
firstFocusable.focus();
return () => {
element.removeEventListener('keydown', handleTabKey);
};
}, [ref]);
}
7. Interviewer: How do you implement CI/CD and automated testing for React applications?
Candidate: Here's my approach to implementing CI/CD and automated testing:
# .github/workflows/main.yml
name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install Dependencies
run: npm ci
- name: Run Linter
run: npm run lint
- name: Run Tests
run: npm run test:ci
- name: Build
run: npm run build
- name: E2E Tests
uses: cypress-io/github-action@v2
with:
start: npm start
wait-on: 'http://localhost:3000'
And the corresponding test setup:
// jest.setup.js
import '@testing-library/jest-dom';
import { server } from './src/mocks/server';
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
// cypress/e2e/auth.spec.js
describe('Authentication Flow', () => {
beforeEach(() => {
cy.intercept('POST', '/api/login', {
statusCode: 200,
body: { token: 'fake-token' }
}).as('loginRequest');
});
it('successfully logs in', () => {
cy.visit('/login');
cy.get('[data-testid="email-input"]').type('user@example.com');
cy.get('[data-testid="password-input"]').type('password123');
cy.get('[data-testid="login-button"]').click();
cy.wait('@loginRequest');
cy.url().should('include', '/dashboard');
});
});
// src/components/__tests__/Button.test.jsx
import { render, fireEvent } from '@testing-library/react';
describe('Button Component', () => {
it('handles different states correctly', () => {
const handleClick = jest.fn();
const { getByRole, rerender } = render(
<Button onClick={handleClick}>Click Me</Button>
);
const button = getByRole('button');
fireEvent.click(button);
expect(handleClick).toHaveBeenCalledTimes(1);
rerender(<Button loading>Click Me</Button>);
expect(button).toBeDisabled();
expect(button).toHaveAttribute('aria-busy', 'true');
});
});
8. Interviewer: How do you handle internationalization (i18n) in large React applications?
Candidate: I implement internationalization using a comprehensive approach that handles both static and dynamic content. Here's my implementation:
// i18n/config.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en',
interpolation: {
escapeValue: false
},
react: {
useSuspense: true
}
});
// hooks/useLocalization.js
function useLocalization() {
const { i18n, t } = useTranslation();
const [loading, setLoading] = useState(false);
const changeLanguage = async (lang) => {
setLoading(true);
try {
await i18n.changeLanguage(lang);
// Update document attributes
document.documentElement.lang = lang;
document.documentElement.dir = i18n.dir(lang);
} finally {
setLoading(false);
}
};
return { t, currentLanguage: i18n.language, changeLanguage, loading };
}
// components/LocalizedApp.jsx
function LocalizedApp() {
const { t, currentLanguage, changeLanguage } = useLocalization();
return (
<div>
<header>
<LanguageSelector
value={currentLanguage}
onChange={changeLanguage}
/>
</header>
<main>
<h1>{t('welcome.title')}</h1>
<p>{t('welcome.description')}</p>
{/* Handling plurals */}
<p>
{t('items.count', { count: 5 })}
</p>
{/* Handling formatted numbers */}
<p>
{t('price.display', {
price: new Intl.NumberFormat(currentLanguage, {
style: 'currency',
currency: 'USD'
}).format(99.99)
})}
</p>
{/* Handling dates */}
<p>
{t('date.display', {
date: new Intl.DateTimeFormat(currentLanguage).format(new Date())
})}
</p>
</main>
</div>
);
}
// Example translation files
const translations = {
en: {
welcome: {
title: 'Welcome to our app',
description: 'Start exploring our features'
},
items: {
count: '{{count}} item',
count_plural: '{{count}} items'
},
price: {
display: 'Price: {{price}}'
},
date: {
display: 'Current date: {{date}}'
}
},
es: {
welcome: {
title: 'Bienvenido a nuestra aplicación',
description: 'Empieza a explorar nuestras funciones'
},
items: {
count: '{{count}} artículo',
count_plural: '{{count}} artículos'
},
price: {
display: 'Precio: {{price}}'
},
date: {
display: 'Fecha actual: {{date}}'
}
}
};
This completes our comprehensive guide to React.js interview questions across different experience levels. Each section has covered crucial aspects of React development, from basic concepts to advanced architectural patterns.
For interviewers, these questions help assess a candidate's depth of knowledge and practical experience. For candidates, they serve as a roadmap for preparation and skill development.
Remember that real-world application often requires combining multiple concepts and adapting them to specific use cases. The best developers not only understand these concepts individually but can also effectively combine them to create robust, maintainable applications.
Conclusion
This comprehensive guide covers 24 essential React.js interview questions across three experience levels. While these questions provide a strong foundation for interview preparation, remember that React development is constantly evolving. The key to success in React interviews isn't just memorizing answers, but understanding the underlying concepts and being able to apply them in real-world scenarios.
Some key takeaways from this guide:
Junior developers should focus on mastering React fundamentals and common patterns
Mid-level developers need to demonstrate deeper understanding of performance optimization and state management
Senior developers should be prepared to discuss architecture decisions and complex technical challenges
Quick Reference: All Questions Covered
Entry Level (1-2 Years Experience)
Can you explain what React.js is and its core features?
What's the difference between state and props in React?
How do you handle events in React?
What is JSX and why do we use it?
Can you explain the component lifecycle in React?
What are hooks in React and why were they introduced?
How do you handle forms in React?
What is the significance of keys in React lists?
Mid Level (2-4 Years Experience)
Explain React's Virtual DOM and reconciliation process in detail
How do you handle state management in large React applications?
What are React error boundaries and how do you implement them?
Explain React's performance optimization techniques you've used in production
How do you handle API integration and data fetching in React?
Explain your approach to testing React components
How do you handle routing in React applications?
What's your experience with React's Server Components?
Senior Level (4+ Years Experience)
How would you architect a large-scale React application from scratch?
Explain your approach to handling complex state management scenarios in React
How do you optimize React applications for performance at scale?
How do you handle micro-frontend architecture with React?
Explain your approach to handling real-time data and WebSocket connections in React
How do you handle accessibility (a11y) in React applications?
How do you implement CI/CD and automated testing for React applications?
How do you handle internationalization (i18n) in large React applications?
Remember that interview success isn't just about knowing the answers—it's about understanding the context and being able to discuss trade-offs and alternatives. Practice explaining your reasoning and be prepared to dive deep into any of these topics.
Keep learning and stay updated with React's evolving ecosystem. The React documentation, community blogs, and hands-on practice are your best resources for continuing education.
Good luck with your interviews!
Connect with me
If you have any questions about my projects or want to discuss potential collaboration opportunities, please don't hesitate to connect with me. You can reach me through the following channels:
Email: bodheeshvc.developer@gmail.com
LinkedIn: linkedin.com/in/bodheeshvc
Youtube: youtube.com/@BodhiTechTalks
Medium: medium.com/@bodheeshvc.developer
I'm always happy to connect with other professionals in the tech industry and discuss ways to work together. Feel free to reach out and let's see how we can help each other grow and succeed!