React Architecture Interview Questions and Answers for Technical Leads
Interview preparation · Technical guide
React Architecture: Interview Questions and Answers
React components, state, rendering, application architecture, and testing. React 19 changes and legacy tooling are distinguished from older examples.
Examples are independent teaching snippets and may require application types, imports, packages, schema, and configuration. Framework behavior is version-dependent. Corrections address identified issues; the complete source code collection has not been compiled or integration-tested.
1. What is React?
Answer: React is a JavaScript library developed by Facebook (Meta) for building user interfaces, particularly single-page applications. It's component-based and follows a declarative programming paradigm.
Key Points: - Library, not framework: React focuses on UI components, unlike Angular which is a full framework - Declarative: You describe what you want, React handles the DOM updates - Component-based: UI is broken down into reusable, self-contained components - Virtual DOM: Uses a virtual representation of the DOM for efficient updates
Example:
import React from 'react';
function App() {
return (
<div className="app">
<h1>Hello React!</h1>
<p>Welcome to my React application</p>
</div>
);
}
export default App;
2. What are the main features of React?
Answer: React's main features include:
- Virtual DOM: Efficient DOM manipulation
- JSX: JavaScript XML for component syntax
- Component-based architecture: Reusable UI components
- Unidirectional data flow: Props flow down, events flow up
- React Hooks: State and lifecycle management in functional components
- Server-side rendering: Next.js and similar frameworks
- Rich ecosystem: Large community and third-party libraries
Example showcasing multiple features:
import React, { useState, useEffect } from 'react';
// Component-based architecture
function UserProfile({ user, onUpdate }) {
// Hooks for state management
const [isEditing, setIsEditing] = useState(false);
const [name, setName] = useState(user.name);
// Lifecycle management with useEffect
useEffect(() => {
document.title = `${user.name}'s Profile`;
}, [user.name]);
// Event handling
const handleSave = () => {
onUpdate({ ...user, name });
setIsEditing(false);
};
// JSX syntax
return (
<div className="user-profile">
{isEditing ? (
<input
value={name}
onChange={(e) => setName(e.target.value)}
onBlur={handleSave}
/>
) : (
<h2 onClick={() => setIsEditing(true)}>{user.name}</h2>
)}
</div>
);
}
3. What is JSX?
Answer: JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code within JavaScript. It's not HTML but gets transformed into JavaScript function calls.
Key Points:
- Not HTML: JSX is closer to JavaScript than HTML
- Must return single element: All JSX must be wrapped in a single parent
- CamelCase attributes: class becomes className, onclick becomes onClick
- JavaScript expressions: Use {} to embed JavaScript
Example:
import React from 'react';
function Greeting({ name, isLoggedIn }) {
// JSX with JavaScript expressions
const message = isLoggedIn ? `Welcome back, ${name}!` : 'Please log in';
// JSX with conditional rendering
return (
<div className="greeting">
<h1>{message}</h1>
{isLoggedIn && <button onClick={() => console.log('Logout')}>Logout</button>}
{!isLoggedIn && <button onClick={() => console.log('Login')}>Login</button>}
</div>
);
}
// JSX transformation (what it becomes):
// React.createElement('div', { className: 'greeting' },
// React.createElement('h1', null, message),
// isLoggedIn && React.createElement('button', { onClick: () => console.log('Logout') }, 'Logout')
// );
4. How does the virtual DOM work in React?
Answer: The Virtual DOM is a lightweight copy of the actual DOM that React uses to optimize rendering performance.
Process: 1. Initial Render: React creates a Virtual DOM tree 2. State Change: When state changes, React creates a new Virtual DOM tree 3. Diffing: React compares the new Virtual DOM with the previous one 4. Reconciliation: Only the differences are applied to the actual DOM
Example demonstrating Virtual DOM efficiency:
import React, { useState } from 'react';
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React', completed: false },
{ id: 2, text: 'Build an app', completed: false },
{ id: 3, text: 'Deploy to production', completed: false }
]);
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
};
return (
<ul>
{todos.map(todo => (
<li
key={todo.id}
onClick={() => toggleTodo(todo.id)}
style={{
textDecoration: todo.completed ? 'line-through' : 'none',
cursor: 'pointer'
}}
>
{todo.text}
</li>
))}
</ul>
);
}
// Virtual DOM optimization: Only the clicked <li> element gets re-rendered,
// not the entire list, even though the state changes
5. What are components in React?
Answer: Components are the building blocks of React applications. They are reusable, self-contained pieces of UI that can accept props and manage their own state.
Types of Components: - Functional Components: Modern approach using hooks - Class Components: Traditional approach using lifecycle methods - Higher-Order Components (HOC): Functions that enhance components - Render Props: Components that receive functions as props
Example:
import React from 'react';
// Functional Component
function Button({ text, onClick, variant = 'primary' }) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{text}
</button>
);
}
// Class Component
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState(prevState => ({ count: prevState.count + 1 }));
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<Button text="Increment" onClick={this.increment} />
</div>
);
}
}
// Higher-Order Component
function withLoading(WrappedComponent) {
return function WithLoadingComponent({ isLoading, ...props }) {
if (isLoading) {
return <div>Loading...</div>;
}
return <WrappedComponent {...props} />;
};
}
const ButtonWithLoading = withLoading(Button);
6. What is the difference between functional and class components?
Answer: The main differences are syntax, state management, lifecycle methods, and performance characteristics.
| Aspect | Functional Components | Class Components |
|---|---|---|
| Syntax | Function declaration | Class with render method |
| State | useState hook | this.state |
| Lifecycle | useEffect hook | componentDidMount, etc. |
| Performance | Can be optimized with React.memo | ShouldComponentUpdate |
| This binding | No this context | Requires binding or arrow functions |
Example comparison:
import React, { useState, useEffect } from 'react';
// Functional Component (Modern approach)
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser(userId).then(userData => {
setUser(userData);
setLoading(false);
});
}, [userId]);
if (loading) return <div>Loading...</div>;
if (!user) return <div>User not found</div>;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// Class Component (Legacy approach)
class UserProfileClass extends React.Component {
constructor(props) {
super(props);
this.state = {
user: null,
loading: true
};
}
componentDidMount() {
this.fetchUserData();
}
componentDidUpdate(prevProps) {
if (prevProps.userId !== this.props.userId) {
this.fetchUserData();
}
}
fetchUserData = async () => {
this.setState({ loading: true });
const userData = await fetchUser(this.props.userId);
this.setState({ user: userData, loading: false });
};
render() {
const { user, loading } = this.state;
if (loading) return <div>Loading...</div>;
if (!user) return <div>User not found</div>;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
}
7. What are props in React?
Answer: Props (properties) are read-only data passed from parent to child components. They are immutable and help create reusable components.
Key Points: - Read-only: Child components cannot modify props - Immutable: Props should not be changed by the receiving component - Can be any type: Strings, numbers, objects, functions, JSX - Default values: Can be set using defaultProps
Example:
import React from 'react';
// Component receiving props
function ProductCard({
name,
price,
image,
onAddToCart,
isOnSale = false,
children
}) {
return (
<div className="product-card">
<img src={image} alt={name} />
<h3>{name}</h3>
<p className={isOnSale ? 'sale-price' : 'regular-price'}>
${price}
</p>
{isOnSale && <span className="sale-badge">SALE!</span>}
<button onClick={() => onAddToCart({ name, price })}>
Add to Cart
</button>
{children} {/* Render children prop */}
</div>
);
}
// Setting default props
ProductCard.defaultProps = {
isOnSale: false
};
// Parent component passing props
function ProductList() {
const handleAddToCart = (product) => {
console.log('Adding to cart:', product);
};
return (
<div className="product-grid">
<ProductCard
name="Laptop"
price={999}
image="/laptop.jpg"
onAddToCart={handleAddToCart}
isOnSale={true}
>
<p>Free shipping included!</p>
</ProductCard>
<ProductCard
name="Mouse"
price={25}
image="/mouse.jpg"
onAddToCart={handleAddToCart}
/>
</div>
);
}
8. What is state in React?
Answer: State is a built-in object that contains data specific to a component. When state changes, the component re-renders.
Key Points: - Component-specific: Each component manages its own state - Mutable: Can be updated using setState or state setters - Triggers re-render: State changes cause component to re-render - Asynchronous: State updates may be batched for performance
Example:
import React, { useState } from 'react';
function ShoppingCart() {
// Multiple state variables
const [items, setItems] = useState([]);
const [total, setTotal] = useState(0);
const [isOpen, setIsOpen] = useState(false);
const addItem = (newItem) => {
setItems(prevItems => [...prevItems, newItem]);
setTotal(prevTotal => prevTotal + newItem.price);
};
const removeItem = (itemId) => {
setItems(prevItems => {
const itemToRemove = prevItems.find(item => item.id === itemId);
setTotal(prevTotal => prevTotal - itemToRemove.price);
return prevItems.filter(item => item.id !== itemId);
});
};
const clearCart = () => {
setItems([]);
setTotal(0);
};
return (
<div className="shopping-cart">
<button onClick={() => setIsOpen(!isOpen)}>
Cart ({items.length}) - ${total}
</button>
{isOpen && (
<div className="cart-dropdown">
{items.length === 0 ? (
<p>Your cart is empty</p>
) : (
<>
{items.map(item => (
<div key={item.id} className="cart-item">
<span>{item.name}</span>
<span>${item.price}</span>
<button onClick={() => removeItem(item.id)}>Remove</button>
</div>
))}
<button onClick={clearCart}>Clear Cart</button>
</>
)}
</div>
)}
</div>
);
}
9. How do you update state in React?
Answer: State can be updated using setState (class components) or state setters (functional components). Updates can be synchronous or asynchronous.
Methods:
1. Direct value: setState(newValue)
2. Function-based: setState(prevState => newState)
3. Object spread: setState({ ...prevState, newProperty: value })
Example:
import React, { useState } from 'react';
function UserForm() {
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
email: '',
age: 0
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
// Method 1: Direct value update
const resetForm = () => {
setFormData({
firstName: '',
lastName: '',
email: '',
age: 0
});
setErrors({});
};
// Method 2: Function-based update
const updateField = (field, value) => {
setFormData(prevData => ({
...prevData,
[field]: value
}));
// Clear error when user starts typing
if (errors[field]) {
setErrors(prevErrors => ({
...prevErrors,
[field]: ''
}));
}
};
// Method 3: Complex state update
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000));
// Update multiple state properties
setFormData(prevData => ({
...prevData,
submitted: true
}));
setErrors({});
setIsSubmitting(false);
} catch (error) {
setErrors({ submit: 'Submission failed' });
setIsSubmitting(false);
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={formData.firstName}
onChange={(e) => updateField('firstName', e.target.value)}
placeholder="First Name"
/>
{errors.firstName && <span className="error">{errors.firstName}</span>}
<input
type="text"
value={formData.lastName}
onChange={(e) => updateField('lastName', e.target.value)}
placeholder="Last Name"
/>
<input
type="email"
value={formData.email}
onChange={(e) => updateField('email', e.target.value)}
placeholder="Email"
/>
<input
type="number"
value={formData.age}
onChange={(e) => updateField('age', parseInt(e.target.value) || 0)}
placeholder="Age"
/>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
<button type="button" onClick={resetForm}>
Reset
</button>
</form>
);
}
10. What is the use of useState hook?
Answer: useState is a React hook that allows functional components to manage state. It returns an array with the current state value and a function to update it.
Syntax: const [state, setState] = useState(initialValue)
Key Points: - Initial value: Can be a value or a function (lazy initialization) - Array destructuring: Returns [state, setter] - Re-renders: Component re-renders when state changes - Batching: Multiple setState calls may be batched
Example:
import React, { useState } from 'react';
function Counter() {
// Basic usage
const [count, setCount] = useState(0);
// Object state
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
// Array state
const [items, setItems] = useState([]);
// Boolean state
const [isVisible, setIsVisible] = useState(false);
// Lazy initialization (function as initial value)
const [expensiveValue, setExpensiveValue] = useState(() => {
// This function runs only once during initialization
return computeExpensiveValue();
});
const increment = () => {
setCount(prevCount => prevCount + 1);
};
const updateUser = (field, value) => {
setUser(prevUser => ({
...prevUser,
[field]: value
}));
};
const addItem = (newItem) => {
setItems(prevItems => [...prevItems, newItem]);
};
const toggleVisibility = () => {
setIsVisible(prev => !prev);
};
return (
<div>
<h2>Count: {count}</h2>
<button onClick={increment}>Increment</button>
<div>
<input
value={user.name}
onChange={(e) => updateUser('name', e.target.value)}
placeholder="Name"
/>
<input
value={user.email}
onChange={(e) => updateUser('email', e.target.value)}
placeholder="Email"
/>
</div>
<button onClick={() => addItem({ id: Date.now(), name: 'New Item' })}>
Add Item
</button>
<button onClick={toggleVisibility}>
{isVisible ? 'Hide' : 'Show'} Content
</button>
{isVisible && (
<div>
<p>User: {user.name}</p>
<p>Items: {items.length}</p>
</div>
)}
</div>
);
}
function computeExpensiveValue() {
// Simulate expensive computation
return Math.random() * 1000;
}
11. What is useEffect hook and how does it work?
useEffect synchronizes a committed component with an external system. List every reactive dependency; cleanup runs before the effect is re-established after dependency changes and on unmount. Strict Mode adds a development-only setup/cleanup check. An empty dependency array does not promise a single execution over the application lifetime. Effects do not run during server rendering. Reference: useEffect.
12. What are controlled and uncontrolled components?
Answer: Controlled components have their value controlled by React state, while uncontrolled components manage their own internal state.
Controlled Components: - Value is controlled by React state - onChange handler updates state - Single source of truth - Better for form validation
Uncontrolled Components: - Use refs to access DOM values - Component manages its own state - Less code but less control - Good for simple forms
Example:
import React, { useState, useRef } from 'react';
// Controlled Component
function ControlledForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Controlled form data:', formData);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
<textarea
name="message"
value={formData.message}
onChange={handleChange}
placeholder="Message"
/>
<button type="submit">Submit</button>
</form>
);
}
// Uncontrolled Component
function UncontrolledForm() {
const nameRef = useRef();
const emailRef = useRef();
const messageRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
const formData = {
name: nameRef.current.value,
email: emailRef.current.value,
message: messageRef.current.value
};
console.log('Uncontrolled form data:', formData);
};
return (
<form onSubmit={handleSubmit}>
<input
ref={nameRef}
type="text"
placeholder="Name"
/>
<input
ref={emailRef}
type="email"
placeholder="Email"
/>
<textarea
ref={messageRef}
placeholder="Message"
/>
<button type="submit">Submit</button>
</form>
);
}
// File input (always uncontrolled)
function FileUpload() {
const fileRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
const file = fileRef.current.files[0];
console.log('Selected file:', file);
};
return (
<form onSubmit={handleSubmit}>
<input
ref={fileRef}
type="file"
accept="image/*"
/>
<button type="submit">Upload</button>
</form>
);
}
13. How do you handle events in React?
Answer: React events are handled using camelCase event handlers and synthetic events. React normalizes events across browsers.
Key Points:
- CamelCase: onClick, onChange, onSubmit
- Synthetic events: Cross-browser compatibility
- Event pooling: Events are reused for performance
- Prevent default: e.preventDefault()
- Stop propagation: e.stopPropagation()
Example:
import React, { useState } from 'react';
function EventHandling() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// Basic event handling
const handleClick = (e) => {
console.log('Button clicked!', e);
setCount(prev => prev + 1);
};
// Event with parameters
const handleButtonClick = (id) => (e) => {
console.log(`Button ${id} clicked!`);
};
// Form submission
const handleSubmit = (e) => {
e.preventDefault(); // Prevent form submission
console.log('Form submitted with text:', text);
};
// Input change
const handleChange = (e) => {
setText(e.target.value);
};
// Keyboard events
const handleKeyPress = (e) => {
if (e.key === 'Enter') {
console.log('Enter pressed!');
}
};
// Mouse events
const handleMouseEnter = () => {
console.log('Mouse entered');
};
const handleMouseLeave = () => {
console.log('Mouse left');
};
// Stop propagation
const handleInnerClick = (e) => {
e.stopPropagation(); // Prevents parent click handler
console.log('Inner div clicked');
};
const handleOuterClick = () => {
console.log('Outer div clicked');
};
return (
<div>
<h2>Event Handling Examples</h2>
{/* Basic click */}
<button onClick={handleClick}>
Clicked {count} times
</button>
{/* Click with parameters */}
<button onClick={handleButtonClick(1)}>Button 1</button>
<button onClick={handleButtonClick(2)}>Button 2</button>
{/* Form submission */}
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={handleChange}
onKeyPress={handleKeyPress}
placeholder="Type and press Enter"
/>
<button type="submit">Submit</button>
</form>
{/* Mouse events */}
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
style={{ padding: '20px', border: '1px solid black' }}
>
Hover over me
</div>
{/* Event propagation */}
<div
onClick={handleOuterClick}
style={{ padding: '20px', border: '1px solid red' }}
>
Outer div
<div
onClick={handleInnerClick}
style={{ padding: '10px', border: '1px solid blue' }}
>
Inner div (click won't bubble up)
</div>
</div>
{/* Conditional event handling */}
<button
onClick={count > 5 ? () => alert('Too many clicks!') : handleClick}
disabled={count > 10}
>
Conditional Click
</button>
</div>
);
}
14. What is prop drilling and how do you avoid it?
Answer: Prop drilling is passing props through multiple levels of components that don't need them, just to reach a deeply nested component.
Problems: - Makes components tightly coupled - Hard to maintain - Reduces reusability - Makes refactoring difficult
Solutions: 1. Context API: Share data across component tree 2. State management libraries: Redux, Zustand 3. Component composition: Pass components as props 4. Custom hooks: Extract logic and state
Example:
import React, { createContext, useContext, useState } from 'react';
// ❌ Prop Drilling Example
function App() {
const [user, setUser] = useState({ name: 'John', theme: 'dark' });
return (
<div>
<Header user={user} setUser={setUser} />
<MainContent user={user} setUser={setUser} />
<Footer user={user} />
</div>
);
}
function Header({ user, setUser }) {
return (
<header>
<Navigation user={user} setUser={setUser} />
</header>
);
}
function Navigation({ user, setUser }) {
return (
<nav>
<UserMenu user={user} setUser={setUser} />
</nav>
);
}
function UserMenu({ user, setUser }) {
return (
<div>
<span>{user.name}</span>
<button onClick={() => setUser({ ...user, theme: 'light' })}>
Toggle Theme
</button>
</div>
);
}
// ✅ Context API Solution
const UserContext = createContext();
function AppWithContext() {
const [user, setUser] = useState({ name: 'John', theme: 'dark' });
return (
<UserContext.Provider value={{ user, setUser }}>
<div>
<Header />
<MainContent />
<Footer />
</div>
</UserContext.Provider>
);
}
function Header() {
return (
<header>
<Navigation />
</header>
);
}
function Navigation() {
return (
<nav>
<UserMenu />
</nav>
);
}
function UserMenu() {
const { user, setUser } = useContext(UserContext);
return (
<div>
<span>{user.name}</span>
<button onClick={() => setUser({ ...user, theme: 'light' })}>
Toggle Theme
</button>
</div>
);
}
// ✅ Component Composition Solution
function AppWithComposition() {
const [user, setUser] = useState({ name: 'John', theme: 'dark' });
const userMenu = (
<UserMenu user={user} setUser={setUser} />
);
return (
<div>
<Header userMenu={userMenu} />
<MainContent />
<Footer />
</div>
);
}
function Header({ userMenu }) {
return (
<header>
<Navigation userMenu={userMenu} />
</header>
);
}
function Navigation({ userMenu }) {
return (
<nav>
{userMenu}
</nav>
);
}
// ✅ Custom Hook Solution
function useUser() {
const [user, setUser] = useState({ name: 'John', theme: 'dark' });
const updateTheme = (theme) => {
setUser(prev => ({ ...prev, theme }));
};
return { user, setUser, updateTheme };
}
function AppWithHook() {
const userHook = useUser();
return (
<UserContext.Provider value={userHook}>
<div>
<Header />
<MainContent />
<Footer />
</div>
</UserContext.Provider>
);
}
15. What is context API?
Answer: Context API is a React feature that allows you to share data across the component tree without prop drilling.
Key Concepts: - Provider: Wraps components and provides data - Consumer: Components that consume the context - useContext hook: Modern way to consume context - Default value: Fallback when no provider exists
Example:
import React, { createContext, useContext, useState } from 'react';
// Create context with default value
const ThemeContext = createContext({
theme: 'light',
toggleTheme: () => {},
colors: {
light: { background: '#fff', text: '#000' },
dark: { background: '#333', text: '#fff' }
}
});
// Custom hook for using theme context
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Provider component
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const colors = {
light: { background: '#fff', text: '#000' },
dark: { background: '#333', text: '#fff' }
};
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
const value = {
theme,
toggleTheme,
colors: colors[theme]
};
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
// Consumer components
function Header() {
const { theme, toggleTheme, colors } = useTheme();
return (
<header style={{
background: colors.background,
color: colors.text,
padding: '1rem'
}}>
<h1>My App</h1>
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'Dark' : 'Light'} Mode
</button>
</header>
);
}
function Content() {
const { colors } = useTheme();
return (
<main style={{
background: colors.background,
color: colors.text,
padding: '2rem'
}}>
<p>This content adapts to the theme!</p>
</main>
);
}
// App with context
function App() {
return (
<ThemeProvider>
<div>
<Header />
<Content />
</div>
</ThemeProvider>
);
}
// Multiple contexts
const UserContext = createContext();
const SettingsContext = createContext();
function AppWithMultipleContexts() {
const [user, setUser] = useState({ name: 'John' });
const [settings, setSettings] = useState({ notifications: true });
return (
<UserContext.Provider value={{ user, setUser }}>
<SettingsContext.Provider value={{ settings, setSettings }}>
<ThemeProvider>
<div>
<Header />
<Content />
</div>
</ThemeProvider>
</SettingsContext.Provider>
</UserContext.Provider>
);
}
React Technical Interview Answers - Technical Lead Level
16. How do you use context in React?
Answer: React Context provides a way to pass data through the component tree without having to pass props down manually at every level. It's useful for sharing data that is considered "global" for a tree of React components.
Key Concepts:
- Context Provider: Wraps components and provides data
- Context Consumer: Components that consume the context data
- useContext Hook: Modern way to consume context
Example Implementation:
// 1. Create a Context
import React, { createContext, useContext, useState } from 'react';
interface UserContextType {
user: User | null;
login: (user: User) => void;
logout: () => void;
}
const UserContext = createContext<UserContextType | undefined>(undefined);
// 2. Create Provider Component
export const UserProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [user, setUser] = useState<User | null>(null);
const login = (userData: User) => {
setUser(userData);
};
const logout = () => {
setUser(null);
};
return (
<UserContext.Provider value={{ user, login, logout }}>
{children}
</UserContext.Provider>
);
};
// 3. Custom Hook for consuming context
export const useUser = () => {
const context = useContext(UserContext);
if (context === undefined) {
throw new Error('useUser must be used within a UserProvider');
}
return context;
};
// 4. Usage in Components
const App = () => {
return (
<UserProvider>
<Header />
<MainContent />
</UserProvider>
);
};
const Header = () => {
const { user, logout } = useUser();
return (
<header>
{user ? (
<div>
<span>Welcome, {user.name}</span>
<button onClick={logout}>Logout</button>
</div>
) : (
<span>Please login</span>
)}
</header>
);
};
Best Practices:
- Use context for truly global state (theme, user authentication, language)
- Avoid overusing context for local component state
- Always provide default values or handle undefined cases
- Use TypeScript for better type safety
17. What are React hooks?
Answer: React hooks are functions that allow you to "hook into" React state and lifecycle features from function components. They were introduced in React 16.8 to allow function components to have state and side effects.
Core Hooks:
useState
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const [user, setUser] = useState<User | null>(null);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(prev => prev + 1)}>Increment (functional)</button>
</div>
);
};
useEffect
import React, { useEffect, useState } from 'react';
const UserProfile = ({ userId }: { userId: string }) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchUser = async () => {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
setUser(userData);
} catch (error) {
console.error('Failed to fetch user:', error);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]); // Dependency array
// Cleanup effect
useEffect(() => {
const interval = setInterval(() => {
console.log('Component is still mounted');
}, 1000);
return () => {
clearInterval(interval);
};
}, []);
if (loading) return <div>Loading...</div>;
if (!user) return <div>User not found</div>;
return <div>{user.name}</div>;
};
Rules of Hooks:
- Only call hooks at the top level
- Only call hooks from React function components or custom hooks
- Hook names must start with "use"
18. Can you create custom hooks? How?
Answer: Custom hooks are JavaScript functions that start with "use" and may call other hooks. They allow you to extract component logic into reusable functions.
Example Custom Hooks:
1. Data Fetching Hook
import { useState, useEffect } from 'react';
interface UseApiResult<T> {
data: T | null;
loading: boolean;
error: string | null;
refetch: () => void;
}
export const useApi = <T>(url: string): UseApiResult<T> => {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, [url]);
return { data, loading, error, refetch: fetchData };
};
// Usage
const UserList = () => {
const { data: users, loading, error, refetch } = useApi<User[]>('/api/users');
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<button onClick={refetch}>Refresh</button>
{users?.map(user => <div key={user.id}>{user.name}</div>)}
</div>
);
};
2. Local Storage Hook
import { useState, useEffect } from 'react';
export const useLocalStorage = <T>(key: string, initialValue: T) => {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(`Error reading localStorage key "${key}":`, error);
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(`Error setting localStorage key "${key}":`, error);
}
};
return [storedValue, setValue] as const;
};
// Usage
const ThemeToggle = () => {
const [theme, setTheme] = useLocalStorage<'light' | 'dark'>('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Current theme: {theme}
</button>
);
};
3. Debounce Hook
import { useState, useEffect } from 'react';
export const useDebounce = <T>(value: T, delay: number): T => {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
};
// Usage
const SearchComponent = () => {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearchTerm) {
// Perform search API call
console.log('Searching for:', debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
);
};
19. What is the useRef hook?
Answer: useRef returns a mutable ref object whose .current property is initialized to the passed argument. The returned object will persist for the full lifetime of the component.
Key Use Cases:
- Accessing DOM elements directly
- Storing mutable values that don't trigger re-renders
- Persisting values between renders
Examples:
1. DOM Element Reference
import React, { useRef, useEffect } from 'react';
const FocusInput = () => {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// Focus the input when component mounts
inputRef.current?.focus();
}, []);
const handleClick = () => {
// Focus input on button click
inputRef.current?.focus();
};
return (
<div>
<input ref={inputRef} type="text" placeholder="Type something..." />
<button onClick={handleClick}>Focus Input</button>
</div>
);
};
2. Storing Previous Values
import React, { useState, useEffect, useRef } from 'react';
const CounterWithPrevious = () => {
const [count, setCount] = useState(0);
const prevCountRef = useRef<number>();
useEffect(() => {
prevCountRef.current = count;
});
const prevCount = prevCountRef.current;
return (
<div>
<h1>Now: {count}, Before: {prevCount}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
3. Storing Mutable Values
import React, { useRef, useEffect } from 'react';
const Timer = () => {
const intervalRef = useRef<NodeJS.Timeout>();
const [count, setCount] = useState(0);
useEffect(() => {
intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []);
const stopTimer = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
return (
<div>
<p>Count: {count}</p>
<button onClick={stopTimer}>Stop Timer</button>
</div>
);
};
4. Video Player Control
import React, { useRef } from 'react';
const VideoPlayer = () => {
const videoRef = useRef<HTMLVideoElement>(null);
const playVideo = () => {
videoRef.current?.play();
};
const pauseVideo = () => {
videoRef.current?.pause();
};
const skipTo = (seconds: number) => {
if (videoRef.current) {
videoRef.current.currentTime = seconds;
}
};
return (
<div>
<video
ref={videoRef}
width="400"
src="https://example.com/video.mp4"
/>
<div>
<button onClick={playVideo}>Play</button>
<button onClick={pauseVideo}>Pause</button>
<button onClick={() => skipTo(30)}>Skip to 30s</button>
</div>
</div>
);
};
20. What is useMemo and when should you use it?
Answer: useMemo is a hook that memoizes the result of a computation. It only recalculates the memoized value when one of its dependencies has changed. This optimization helps avoid expensive calculations on every render.
When to Use:
- Expensive calculations (complex algorithms, data transformations)
- Object/array creation that would cause child components to re-render unnecessarily
- API response processing or data formatting
Examples:
1. Expensive Calculation
import React, { useState, useMemo } from 'react';
const ExpensiveCalculation = ({ numbers }: { numbers: number[] }) => {
const [count, setCount] = useState(0);
// Expensive calculation - only runs when 'numbers' changes
const expensiveResult = useMemo(() => {
console.log('Performing expensive calculation...');
return numbers.reduce((sum, num) => {
// Simulate expensive operation
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += num * Math.random();
}
return sum + result;
}, 0);
}, [numbers]);
return (
<div>
<p>Count: {count}</p>
<p>Expensive Result: {expensiveResult.toFixed(2)}</p>
<button onClick={() => setCount(count + 1)}>
Increment (won't trigger calculation)
</button>
</div>
);
};
2. Filtered and Sorted Data
import React, { useState, useMemo } from 'react';
interface User {
id: number;
name: string;
age: number;
department: string;
}
const UserList = ({ users }: { users: User[] }) => {
const [searchTerm, setSearchTerm] = useState('');
const [sortBy, setSortBy] = useState<'name' | 'age'>('name');
// Memoized filtered and sorted users
const filteredAndSortedUsers = useMemo(() => {
console.log('Filtering and sorting users...');
return users
.filter(user =>
user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.department.toLowerCase().includes(searchTerm.toLowerCase())
)
.sort((a, b) => {
if (sortBy === 'name') {
return a.name.localeCompare(b.name);
}
return a.age - b.age;
});
}, [users, searchTerm, sortBy]);
return (
<div>
<input
type="text"
placeholder="Search users..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<select value={sortBy} onChange={(e) => setSortBy(e.target.value as 'name' | 'age')}>
<option value="name">Sort by Name</option>
<option value="age">Sort by Age</option>
</select>
<ul>
{filteredAndSortedUsers.map(user => (
<li key={user.id}>
{user.name} - {user.age} - {user.department}
</li>
))}
</ul>
</div>
);
};
3. Complex Object Creation
import React, { useState, useMemo } from 'react';
const ChartComponent = ({ data, width, height }: {
data: number[],
width: number,
height: number
}) => {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
// Memoized chart configuration
const chartConfig = useMemo(() => {
console.log('Creating chart configuration...');
return {
width,
height,
theme: {
light: {
backgroundColor: '#ffffff',
textColor: '#000000',
gridColor: '#e0e0e0'
},
dark: {
backgroundColor: '#1a1a1a',
textColor: '#ffffff',
gridColor: '#333333'
}
}[theme],
data: data.map((value, index) => ({
x: (index / (data.length - 1)) * width,
y: height - (value / Math.max(...data)) * height
}))
};
}, [data, width, height, theme]);
return (
<div>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
<svg width={width} height={height} style={{ backgroundColor: chartConfig.theme.backgroundColor }}>
{/* Chart rendering logic using chartConfig */}
{chartConfig.data.map((point, index) => (
<circle
key={index}
cx={point.x}
cy={point.y}
r="3"
fill={chartConfig.theme.textColor}
/>
))}
</svg>
</div>
);
};
Performance Comparison:
// Without useMemo - recalculates on every render
const BadExample = ({ data }: { data: number[] }) => {
const [count, setCount] = useState(0);
// This runs on every render!
const expensiveValue = data.reduce((sum, num) => sum + num * 2, 0);
return (
<div>
<p>Count: {count}</p>
<p>Expensive Value: {expensiveValue}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
// With useMemo - only recalculates when data changes
const GoodExample = ({ data }: { data: number[] }) => {
const [count, setCount] = useState(0);
// This only runs when 'data' changes
const expensiveValue = useMemo(() => {
return data.reduce((sum, num) => sum + num * 2, 0);
}, [data]);
return (
<div>
<p>Count: {count}</p>
<p>Expensive Value: {expensiveValue}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
21. What is useCallback and why is it useful?
Answer: useCallback returns a memoized version of the callback function that only changes if one of the dependencies has changed. It's useful for preventing unnecessary re-renders of child components that rely on reference equality.
When to Use:
- Passing callbacks to optimized child components that rely on reference equality
- Preventing infinite loops in useEffect dependencies
- Optimizing performance when callbacks are used as dependencies
Examples:
1. Preventing Child Re-renders
import React, { useState, useCallback } from 'react';
// Child component that re-renders when props change
const ExpensiveChild = React.memo(({ onIncrement, onDecrement }: {
onIncrement: () => void;
onDecrement: () => void;
}) => {
console.log('ExpensiveChild rendered');
return (
<div>
<button onClick={onIncrement}>Increment</button>
<button onClick={onDecrement}>Decrement</button>
</div>
);
});
// Parent component
const ParentComponent = () => {
const [count, setCount] = useState(0);
const [otherState, setOtherState] = useState(0);
// Without useCallback - new function created on every render
const handleIncrement = () => {
setCount(c => c + 1);
};
const handleDecrement = () => {
setCount(c => c - 1);
};
// With useCallback - function only recreated when dependencies change
const memoizedIncrement = useCallback(() => {
setCount(c => c + 1);
}, []); // Empty dependency array since it doesn't depend on any values
const memoizedDecrement = useCallback(() => {
setCount(c => c - 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<p>Other State: {otherState}</p>
{/* This will re-render on every parent render */}
<ExpensiveChild onIncrement={handleIncrement} onDecrement={handleDecrement} />
{/* This will only re-render when count changes */}
<ExpensiveChild onIncrement={memoizedIncrement} onDecrement={memoizedDecrement} />
<button onClick={() => setOtherState(otherState + 1)}>
Update Other State
</button>
</div>
);
};
2. Preventing Infinite Loops
import React, { useState, useEffect, useCallback } from 'react';
const UserProfile = ({ userId }: { userId: string }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
// Without useCallback - causes infinite loop
const fetchUser = async () => {
setLoading(true);
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
setUser(data);
} catch (error) {
console.error('Failed to fetch user:', error);
} finally {
setLoading(false);
}
};
// This causes infinite loop because fetchUser is recreated on every render
// useEffect(() => {
// fetchUser();
// }, [fetchUser]); // ❌ Bad!
// With useCallback - prevents infinite loop
const memoizedFetchUser = useCallback(async () => {
setLoading(true);
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
setUser(data);
} catch (error) {
console.error('Failed to fetch user:', error);
} finally {
setLoading(false);
}
}, [userId]); // Only recreate when userId changes
useEffect(() => {
memoizedFetchUser();
}, [memoizedFetchUser]); // ✅ Good!
return (
<div>
{loading ? <p>Loading...</p> : <p>User: {user?.name}</p>}
<button onClick={memoizedFetchUser}>Refresh</button>
</div>
);
};
3. Optimizing Custom Hooks
import React, { useState, useCallback } from 'react';
// Custom hook that returns memoized callbacks
const useCounter = (initialValue: number = 0) => {
const [count, setCount] = useState(initialValue);
const increment = useCallback(() => {
setCount(c => c + 1);
}, []);
const decrement = useCallback(() => {
setCount(c => c - 1);
}, []);
const reset = useCallback(() => {
setCount(initialValue);
}, [initialValue]);
const setValue = useCallback((value: number) => {
setCount(value);
}, []);
return {
count,
increment,
decrement,
reset,
setValue
};
};
// Usage
const CounterApp = () => {
const { count, increment, decrement, reset } = useCounter(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
};
4. Event Handlers with Dependencies
import React, { useState, useCallback } from 'react';
const TodoList = () => {
const [todos, setTodos] = useState<string[]>([]);
const [filter, setFilter] = useState<'all' | 'active' | 'completed'>('all');
// Callback depends on current todos state
const addTodo = useCallback((text: string) => {
setTodos(prevTodos => [...prevTodos, text]);
}, []); // No dependencies needed since we use functional update
// Callback depends on filter state
const filteredTodos = useCallback(() => {
switch (filter) {
case 'active':
return todos.filter(todo => !todo.startsWith('✓ '));
case 'completed':
return todos.filter(todo => todo.startsWith('✓ '));
default:
return todos;
}
}, [todos, filter]); // Depends on todos and filter
const toggleTodo = useCallback((index: number) => {
setTodos(prevTodos =>
prevTodos.map((todo, i) =>
i === index
? (todo.startsWith('✓ ') ? todo.slice(2) : `✓ ${todo}`)
: todo
)
);
}, []); // No dependencies needed
return (
<div>
<div>
<input
type="text"
onKeyPress={(e) => {
if (e.key === 'Enter' && e.currentTarget.value.trim()) {
addTodo(e.currentTarget.value.trim());
e.currentTarget.value = '';
}
}}
placeholder="Add todo..."
/>
</div>
<div>
<button onClick={() => setFilter('all')}>All</button>
<button onClick={() => setFilter('active')}>Active</button>
<button onClick={() => setFilter('completed')}>Completed</button>
</div>
<ul>
{filteredTodos().map((todo, index) => (
<li key={index} onClick={() => toggleTodo(index)}>
{todo}
</li>
))}
</ul>
</div>
);
};
22. What is the difference between useEffect and useLayoutEffect?
Answer: Both hooks are used for side effects, but they differ in timing:
- useEffect: Runs asynchronously after the browser has painted
- useLayoutEffect: Runs synchronously after all DOM mutations but before the browser paints
Key Differences:
| Aspect | useEffect | useLayoutEffect |
|---|---|---|
| Timing | After paint | Before paint |
| Blocking | Non-blocking | Blocking |
| Use Case | Most side effects | DOM measurements, mutations |
| Performance | Better for performance | Can cause performance issues |
Examples:
1. Basic Difference Demonstration
import React, { useState, useEffect, useLayoutEffect } from 'react';
const TimingExample = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('useEffect: After paint');
}, [count]);
useLayoutEffect(() => {
console.log('useLayoutEffect: Before paint');
}, [count]);
console.log('Component render');
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
// Console output order:
// 1. "Component render"
// 2. "useLayoutEffect: Before paint"
// 3. "useEffect: After paint"
2. DOM Measurements (useLayoutEffect)
import React, { useState, useLayoutEffect, useRef } from 'react';
const MeasuredComponent = () => {
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const elementRef = useRef<HTMLDivElement>(null);
// Use useLayoutEffect for DOM measurements to avoid flicker
useLayoutEffect(() => {
if (elementRef.current) {
const rect = elementRef.current.getBoundingClientRect();
setWidth(rect.width);
setHeight(rect.height);
}
}, []);
return (
<div>
<div
ref={elementRef}
style={{
width: '200px',
height: '100px',
border: '1px solid black',
padding: '10px'
}}
>
This element is being measured
</div>
<p>Width: {width}px</p>
<p>Height: {height}px</p>
</div>
);
};
3. Preventing Flicker (useLayoutEffect)
import React, { useState, useLayoutEffect } from 'react';
const FlickerExample = () => {
const [showTooltip, setShowTooltip] = useState(false);
const [tooltipPosition, setTooltipPosition] = useState({ x: 0, y: 0 });
// Bad: Using useEffect causes flicker
useEffect(() => {
if (showTooltip) {
// This runs after paint, causing flicker
const rect = document.getElementById('button')?.getBoundingClientRect();
if (rect) {
setTooltipPosition({
x: rect.left + rect.width / 2,
y: rect.top - 10
});
}
}
}, [showTooltip]);
// Good: Using useLayoutEffect prevents flicker
useLayoutEffect(() => {
if (showTooltip) {
// This runs before paint, preventing flicker
const rect = document.getElementById('button')?.getBoundingClientRect();
if (rect) {
setTooltipPosition({
x: rect.left + rect.width / 2,
y: rect.top - 10
});
}
}
}, [showTooltip]);
return (
<div>
<button
id="button"
onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
>
Hover me
</button>
{showTooltip && (
<div
style={{
position: 'fixed',
left: tooltipPosition.x,
top: tooltipPosition.y,
transform: 'translateX(-50%)',
backgroundColor: 'black',
color: 'white',
padding: '5px',
borderRadius: '3px',
fontSize: '12px'
}}
>
Tooltip content
</div>
)}
</div>
);
};
4. Synchronous DOM Updates (useLayoutEffect)
import React, { useState, useLayoutEffect, useRef } from 'react';
const ScrollPosition = () => {
const [scrollPosition, setScrollPosition] = useState(0);
const containerRef = useRef<HTMLDivElement>(null);
// Preserve scroll position during content changes
useLayoutEffect(() => {
if (containerRef.current) {
const currentScrollTop = containerRef.current.scrollTop;
// Force a reflow to ensure DOM is updated
containerRef.current.offsetHeight;
// Restore scroll position
containerRef.current.scrollTop = currentScrollTop;
}
}, [scrollPosition]);
const handleScroll = () => {
if (containerRef.current) {
setScrollPosition(containerRef.current.scrollTop);
}
};
return (
<div
ref={containerRef}
onScroll={handleScroll}
style={{
height: '200px',
overflow: 'auto',
border: '1px solid #ccc'
}}
>
{Array.from({ length: 50 }, (_, i) => (
<div key={i} style={{ padding: '10px', borderBottom: '1px solid #eee' }}>
Item {i + 1} - Scroll position: {scrollPosition}
</div>
))}
</div>
);
};
5. When to Use Each
// ✅ Use useEffect for:
// - API calls
// - Subscriptions
// - Logging
// - Non-critical DOM updates
useEffect(() => {
// API call
fetch('/api/data').then(setData);
// Subscription
const subscription = eventBus.subscribe(handleEvent);
return () => subscription.unsubscribe();
}, []);
// ✅ Use useLayoutEffect for:
// - DOM measurements
// - Preventing flicker
// - Synchronous DOM updates
// - Critical layout calculations
useLayoutEffect(() => {
// DOM measurement
const rect = elementRef.current?.getBoundingClientRect();
if (rect) {
setPosition({ x: rect.left, y: rect.top });
}
// Synchronous DOM update
if (elementRef.current) {
elementRef.current.style.transform = `translate(${position.x}px, ${position.y}px)`;
}
}, [position]);
23. How do you optimize performance in React applications?
Answer: React performance optimization involves several strategies to minimize unnecessary re-renders and improve application speed.
Key Optimization Techniques:
- Code Splitting and Lazy Loading
// From the codebase - Lazy loading components
import { Suspense, lazy } from 'react';
const SmChat = lazy(() => import('@components/atomic/SmChat/SmChat'));
// Usage with Suspense
<Suspense fallback={<></>}>
<SmChat
chatList={chatListState}
handleQueryEntered={fetchChatResponse}
onDeleteChat={handleDeleteChat}
loader={loader}
onUpdateChatFeedback={handleFeedback}
fetchChatThreadResponse={fetchChatThreadResponse}
/>
</Suspense>
- useCallback for Function Memoization
// From the codebase - useShortPolling.ts
const startPolling = useCallback(() => {
if (!isPolling.current) {
isPolling.current = true;
intervalId.current = setInterval(() => {
if (typeof callback === 'function') {
callback();
}
}, interval);
}
}, [callback, interval]);
const stopPolling = useCallback(() => {
if (isPolling.current) {
if (intervalId.current) {
clearInterval(intervalId.current);
intervalId.current = null;
}
isPolling.current = false;
}
}, []);
- useMemo for Expensive Calculations
// Example of useMemo usage
const expensiveValue = useMemo(() => {
return computeExpensiveValue(data);
}, [data]);
- React.memo for Component Memoization
// From the codebase - SMDropdown.tsx
const DropdownElement = ({ initialValue, options, onChange, ...props }) => {
// Component logic
};
export default memo(DropdownElement);
- Bundle Optimization (from vite.config.ts)
// Manual chunk splitting for better caching
rollupOptions: {
output: {
manualChunks: {
'vendor-react': ['react', 'react-dom'],
'vender-msal': ['@azure/msal-react', '@azure/msal-browser'],
'vendor-redux': ['@reduxjs/toolkit', 'react-redux'],
'vendor-router': ['react-router-dom'],
'vendor-axios': ['axios', 'jwt-decode'],
},
},
}
- Debouncing for Performance
// From the codebase - SmDatePicker.tsx
import { useDebouncedCallback } from 'use-debounce';
const handleDateChange = useDebouncedCallback((e: FocusEvent<HTMLInputElement, Element>) => {
// Date change logic
}, 500);
24. What is React.memo?
Answer: React.memo is a higher-order component that memoizes a functional component, preventing unnecessary re-renders when props haven't changed.
How it works:
- It performs a shallow comparison of props
- If props are the same, it skips re-rendering
- If props are different, it re-renders the component
Example from the codebase:
// From SMDropdown.tsx
import { memo } from 'react';
const DropdownElement = ({ initialValue, options, onChange, ...props }) => {
// Component logic
return (
<DropDownList
data={formattedOptions}
value={formattedOptions?.find((item) => item[valueField] === initialValue) || null}
onChange={handleChange}
// ... other props
/>
);
};
export default memo(DropdownElement);
Custom Comparison Function:
const MyComponent = memo(({ data, onUpdate }) => {
return <div>{/* component content */}</div>;
}, (prevProps, nextProps) => {
// Custom comparison logic
return prevProps.data.id === nextProps.data.id;
});
25. What is the difference between React.memo and useMemo?
React.memo is a performance optimization that can skip rendering when props compare equal (Object.is by default). State or consumed context can still trigger updates, and React may render anyway. useMemo caches a calculation result; useCallback caches a function identity. None guarantees correctness or should be required to keep an application working. Measure before adding memoization. Reference: memo.
26. What is reconciliation in React?
Answer: Reconciliation is React's algorithm for determining what parts of the UI need to be updated when state or props change.
How it works:
- Virtual DOM Comparison: React compares the new Virtual DOM with the previous one
- Diffing Algorithm: Identifies the minimal set of changes needed
- Batch Updates: Applies changes efficiently to the real DOM
Example from the codebase:
// From Chat.tsx - Multiple state updates
useEffect(() => {
dispatch(setShowClearMessages(chatListState?.length > 0));
}, [chatListState]);
useEffect(() => {
dispatch(setDisabledClearMessages(chatListState?.length === 0 || loader));
}, [chatListState, loader]);
Reconciliation Process:
// React's internal process (simplified)
function reconcileChildren(prevChildren, nextChildren) {
// 1. Compare element types
if (prevChildren.type !== nextChildren.type) {
// Replace entire component
return replaceComponent(prevChildren, nextChildren);
}
// 2. Compare props
if (propsChanged(prevChildren.props, nextChildren.props)) {
// Update props only
return updateProps(prevChildren, nextChildren);
}
// 3. Reconcile children recursively
return reconcileChildren(prevChildren.children, nextChildren.children);
}
Keys and Reconciliation:
// Keys help React identify which items changed
{chatListState.map((chat, index) => (
<ChatItem
key={chat.id} // Unique key for efficient reconciliation
chat={chat}
onDelete={handleDeleteChat}
/>
))}
27. What are keys in React and why are they important?
Answer: Keys are special props that help React identify which items have changed, been added, or been removed in lists.
Why Keys are Important:
- Efficient Reconciliation: Helps React identify which items changed
- Preserve Component State: Maintains component state across re-renders
- Performance: Avoids unnecessary re-renders of unchanged items
Example from the codebase:
// From Chat.tsx - Using unique keys for chat items
{chatListState.map((chat) => (
<ChatItem
key={chat.id} // Unique identifier
chat={chat}
onDelete={handleDeleteChat}
/>
))}
Good vs Bad Key Examples:
// ❌ Bad - Using array index
{items.map((item, index) => (
<ListItem key={index} item={item} />
))}
// ✅ Good - Using unique ID
{items.map((item) => (
<ListItem key={item.id} item={item} />
))}
// ✅ Good - Using stable unique value
{items.map((item) => (
<ListItem key={`${item.type}-${item.id}`} item={item} />
))}
Keys in Dynamic Lists:
// From the codebase pattern - Toast notifications
const noti = {
...action.payload,
id: `${getRandomId()}-local-${getRandomId()}`, // Unique key generation
duration: action.payload?.duration || 4000,
};
// Usage in list
{toast.map((notification) => (
<Toast
key={notification.id} // Unique key for each notification
notification={notification}
onRemove={removeToast}
/>
))}
28. How do you handle forms in React?
Answer: React forms can be handled using controlled components, uncontrolled components, or form libraries like react-hook-form.
1. Controlled Components (Traditional Approach):
// From the codebase pattern - SmDatePicker.tsx
const SmDatePicker = ({ value, onChange, ...props }) => {
const [dateValue, setDateValue] = useState(value);
const handleDateChange = (e) => {
const newDate = e.target.value;
setDateValue(newDate);
onChange?.(newDate);
};
return (
<DatePicker
value={dateValue}
onChange={handleDateChange}
{...props}
/>
);
};
2. Using react-hook-form (Recommended):
// From the codebase - FeedbackForm.tsx pattern
import { useForm } from 'react-hook-form';
const FeedbackForm = ({ onSubmit }) => {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmitForm = (data) => {
onSubmit(data);
};
return (
<form onSubmit={handleSubmit(onSubmitForm)}>
<input
{...register('title', { required: 'Title is required' })}
placeholder="Title"
/>
{errors.title && <span>{errors.title.message}</span>}
<textarea
{...register('content', { required: 'Content is required' })}
placeholder="Description"
/>
{errors.content && <span>{errors.content.message}</span>}
<button type="submit">Submit</button>
</form>
);
};
3. Custom Form Hook:
// Custom form hook pattern
const useForm = (initialValues) => {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const handleChange = (name, value) => {
setValues(prev => ({ ...prev, [name]: value }));
// Clear error when user starts typing
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: '' }));
}
};
const handleSubmit = (onSubmit) => (e) => {
e.preventDefault();
onSubmit(values);
};
return { values, errors, handleChange, handleSubmit };
};
29. How do you perform form validation in React?
Answer: Form validation can be implemented using various approaches including built-in HTML validation, custom validation, or form libraries.
1. Built-in HTML Validation:
const SimpleForm = () => {
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
// Process form data
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
required
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
placeholder="Email"
/>
<input
type="password"
required
minLength={8}
placeholder="Password"
/>
<button type="submit">Submit</button>
</form>
);
};
2. Custom Validation with useState:
const ValidatedForm = () => {
const [formData, setFormData] = useState({
email: '',
password: ''
});
const [errors, setErrors] = useState({});
const validateForm = () => {
const newErrors = {};
// Email validation
if (!formData.email) {
newErrors.email = 'Email is required';
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
newErrors.email = 'Email is invalid';
}
// Password validation
if (!formData.password) {
newErrors.password = 'Password is required';
} else if (formData.password.length < 8) {
newErrors.password = 'Password must be at least 8 characters';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validateForm()) {
// Submit form
console.log('Form submitted:', formData);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="email"
value={formData.email}
onChange={(e) => setFormData({...formData, email: e.target.value})}
placeholder="Email"
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
<div>
<input
type="password"
value={formData.password}
onChange={(e) => setFormData({...formData, password: e.target.value})}
placeholder="Password"
/>
{errors.password && <span className="error">{errors.password}</span>}
</div>
<button type="submit">Submit</button>
</form>
);
};
3. Using react-hook-form with Validation:
import { useForm } from 'react-hook-form';
const HookFormValidation = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log('Form data:', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input
{...register('email', {
required: 'Email is required',
pattern: {
value: /\S+@\S+\.\S+/,
message: 'Please enter a valid email'
}
})}
placeholder="Email"
/>
{errors.email && <span>{errors.email.message}</span>}
</div>
<div>
<input
type="password"
{...register('password', {
required: 'Password is required',
minLength: {
value: 8,
message: 'Password must be at least 8 characters'
}
})}
placeholder="Password"
/>
{errors.password && <span>{errors.password.message}</span>}
</div>
<button type="submit">Submit</button>
</form>
);
};
4. Real-time Validation:
const RealTimeValidation = () => {
const [email, setEmail] = useState('');
const [emailError, setEmailError] = useState('');
const validateEmail = (value) => {
if (!value) {
setEmailError('Email is required');
} else if (!/\S+@\S+\.\S+/.test(value)) {
setEmailError('Please enter a valid email');
} else {
setEmailError('');
}
};
const handleEmailChange = (e) => {
const value = e.target.value;
setEmail(value);
validateEmail(value);
};
return (
<div>
<input
type="email"
value={email}
onChange={handleEmailChange}
placeholder="Email"
/>
{emailError && <span className="error">{emailError}</span>}
</div>
);
};
30. What is Redux?
Answer: Redux is a predictable state management library for JavaScript applications, commonly used with React.
Core Concepts:
- Store: Single source of truth for application state
- Actions: Plain objects describing what happened
- Reducers: Pure functions that specify how state changes
- Dispatch: Method to send actions to the store
Example from the codebase:
// Store configuration (from store.ts)
import { configureStore } from '@reduxjs/toolkit';
import { authReducer } from './authReducer';
import { appReducer } from './appReducer';
export const store = configureStore({
reducer: {
auth: authReducer,
app: appReducer,
},
preloadedState,
});
Reducer Example (from authReducer.ts):
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface InitialState {
user: AppUserSelf | null;
userLoading: boolean;
sessionExpired: boolean;
role: RoleEnum;
}
const initialState: InitialState = {
user: null,
userLoading: false,
sessionExpired: true,
role: null,
};
const authSlice = createSlice({
name: 'auth',
initialState: initialState,
reducers: {
setUserLoading: (state, action: PayloadAction<boolean>) => {
state.userLoading = action.payload;
},
setUser: (state, action: PayloadAction<AppUserSelf | null>) => {
state.user = action.payload;
},
logout: (state) => {
localStorage.clear();
state.user = null;
state.sessionExpired = true;
state.role = null;
},
setRole: (state, action: PayloadAction<RoleEnum>) => {
state.role = action.payload;
state.sessionExpired = false;
},
},
});
export const { setUser, setUserLoading, logout, setRole } = authSlice.actions;
export const authReducer = authSlice.reducer;
Using Redux in Components:
// From Chat.tsx
import { useDispatch } from 'react-redux';
import { setShowClearMessages, setDisabledClearMessages } from '@store/appReducer';
const Chat = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(setShowClearMessages(chatListState?.length > 0));
}, [chatListState]);
useEffect(() => {
dispatch(setDisabledClearMessages(chatListState?.length === 0 || loader));
}, [chatListState, loader]);
// Component logic
};
Complete Redux Example:
// Action Types
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';
// Action Creators
const addTodo = (text: string) => ({
type: ADD_TODO,
payload: { id: Date.now(), text, completed: false }
});
const toggleTodo = (id: number) => ({
type: TOGGLE_TODO,
payload: id
});
// Reducer
const todoReducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [...state, action.payload];
case TOGGLE_TODO:
return state.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
);
default:
return state;
}
};
// Component using Redux
const TodoList = () => {
const todos = useSelector(state => state.todos);
const dispatch = useDispatch();
const handleAddTodo = (text) => {
dispatch(addTodo(text));
};
const handleToggleTodo = (id) => {
dispatch(toggleTodo(id));
};
return (
<div>
{todos.map(todo => (
<div key={todo.id}>
<span
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
onClick={() => handleToggleTodo(todo.id)}
>
{todo.text}
</span>
</div>
))}
</div>
);
};
Redux Toolkit Benefits:
- Simplified Setup: Less boilerplate code
- Immutability: Built-in Immer integration
- DevTools: Better debugging experience
- TypeScript Support: Better type safety
31. What are the core principles of Redux?
Answer: Redux follows three core principles:
- Single Source of Truth: The entire application state is stored in one object tree within a single store
- State is Read-Only: The only way to change state is to emit an action
- Changes are Made with Pure Functions: Reducers are pure functions that take previous state and action, and return new state
Example:
// Single source of truth - one store
const store = createStore(rootReducer);
// State is read-only - only actions can change it
const incrementAction = { type: 'INCREMENT' };
store.dispatch(incrementAction);
// Pure function reducer
const counterReducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
};
32. What is the difference between Redux and Context API?
Answer:
| Aspect | Redux | Context API |
|---|---|---|
| Complexity | More complex setup, requires more boilerplate | Simpler, built into React |
| Performance | Optimized for large applications with selective re-rendering | Can cause unnecessary re-renders |
| DevTools | Excellent debugging tools | Limited debugging capabilities |
| Middleware | Supports middleware for side effects | No built-in middleware support |
| Use Case | Large applications with complex state management | Simple state sharing between components |
Example - Context API:
// Simple state sharing
const ThemeContext = createContext();
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
const useTheme = () => useContext(ThemeContext);
Example - Redux:
// More complex but powerful
const themeSlice = createSlice({
name: 'theme',
initialState: { theme: 'light' },
reducers: {
toggleTheme: (state) => {
state.theme = state.theme === 'light' ? 'dark' : 'light';
}
}
});
const store = configureStore({
reducer: {
theme: themeSlice.reducer
}
});
33. What are actions, reducers, and stores in Redux?
Answer:
Actions: Plain JavaScript objects that describe what happened Reducers: Pure functions that specify how state changes in response to actions Store: Object that holds application state and provides methods to access and update it
Example:
// Action - describes what happened
const addTodoAction = {
type: 'ADD_TODO',
payload: {
id: 1,
text: 'Learn Redux',
completed: false
}
};
// Action Creator
const addTodo = (text) => ({
type: 'ADD_TODO',
payload: {
id: Date.now(),
text,
completed: false
}
});
// Reducer - pure function that handles state changes
const todoReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, action.payload];
case 'TOGGLE_TODO':
return state.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
);
default:
return state;
}
};
// Store - holds state and provides methods
const store = createStore(todoReducer);
// Dispatch actions to update state
store.dispatch(addTodo('Learn Redux'));
34. How do you connect React with Redux?
Answer: Use react-redux library with Provider and useSelector/useDispatch hooks (modern approach) or connect HOC (legacy approach).
Modern Approach (Hooks):
// App.js
import { Provider } from 'react-redux';
import { store } from './store';
const App = () => (
<Provider store={store}>
<TodoApp />
</Provider>
);
// TodoList.js
import { useSelector, useDispatch } from 'react-redux';
const TodoList = () => {
const todos = useSelector(state => state.todos);
const dispatch = useDispatch();
const handleAddTodo = (text) => {
dispatch(addTodo(text));
};
return (
<div>
{todos.map(todo => (
<div key={todo.id}>{todo.text}</div>
))}
<button onClick={() => handleAddTodo('New Todo')}>
Add Todo
</button>
</div>
);
};
Legacy Approach (connect HOC):
import { connect } from 'react-redux';
const TodoList = ({ todos, addTodo }) => {
return (
<div>
{todos.map(todo => (
<div key={todo.id}>{todo.text}</div>
))}
<button onClick={() => addTodo('New Todo')}>
Add Todo
</button>
</div>
);
};
const mapStateToProps = (state) => ({
todos: state.todos
});
const mapDispatchToProps = (dispatch) => ({
addTodo: (text) => dispatch(addTodo(text))
});
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
35. What is middleware in Redux?
Answer: Middleware provides a way to intercept and modify actions before they reach the reducer. It's useful for logging, async operations, and side effects.
Example:
// Custom middleware
const loggerMiddleware = store => next => action => {
console.log('Previous State:', store.getState());
console.log('Action:', action);
const result = next(action);
console.log('Next State:', store.getState());
return result;
};
// Async middleware
const asyncMiddleware = store => next => action => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState);
}
return next(action);
};
// Apply middleware
const store = createStore(
rootReducer,
applyMiddleware(loggerMiddleware, asyncMiddleware)
);
// Usage
const fetchTodos = () => async (dispatch) => {
dispatch({ type: 'FETCH_TODOS_START' });
try {
const response = await fetch('/api/todos');
const todos = await response.json();
dispatch({ type: 'FETCH_TODOS_SUCCESS', payload: todos });
} catch (error) {
dispatch({ type: 'FETCH_TODOS_ERROR', payload: error.message });
}
};
36. What is Redux Thunk?
Answer: Redux Thunk is middleware that allows action creators to return functions instead of actions, enabling async operations and conditional dispatching.
Example:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
// Async thunk
export const fetchUserData = createAsyncThunk(
'user/fetchUserData',
async (userId, { rejectWithValue }) => {
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
return data;
} catch (error) {
return rejectWithValue(error.message);
}
}
);
// Slice with async thunk
const userSlice = createSlice({
name: 'user',
initialState: {
data: null,
loading: false,
error: null
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUserData.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchUserData.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUserData.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
});
}
});
// Usage in component
const UserProfile = () => {
const dispatch = useDispatch();
const { data, loading, error } = useSelector(state => state.user);
useEffect(() => {
dispatch(fetchUserData(123));
}, [dispatch]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{data?.name}</div>;
};
37. What is Redux Saga?
Answer: Redux Saga is middleware that uses ES6 generators to handle side effects in Redux applications. It's more powerful than Thunk for complex async operations.
Example:
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
// API function
const fetchUserAPI = (userId) =>
fetch(`/api/users/${userId}`).then(res => res.json());
// Saga worker
function* fetchUser(action) {
try {
yield put({ type: 'FETCH_USER_START' });
const user = yield call(fetchUserAPI, action.payload);
yield put({ type: 'FETCH_USER_SUCCESS', payload: user });
} catch (error) {
yield put({ type: 'FETCH_USER_ERROR', payload: error.message });
}
}
// Saga watcher
function* userSaga() {
yield takeLatest('FETCH_USER_REQUEST', fetchUser);
}
// Root saga
function* rootSaga() {
yield all([
userSaga()
]);
}
// Store setup
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
rootReducer,
applyMiddleware(sagaMiddleware)
);
sagaMiddleware.run(rootSaga);
// Action creator
const fetchUserRequest = (userId) => ({
type: 'FETCH_USER_REQUEST',
payload: userId
});
38. What is the use of React Router?
Answer: React Router enables client-side routing in React applications, allowing navigation between different views without page reloads. It provides URL-based navigation, nested routes, and route parameters.
Example:
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
const App = () => (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/users">Users</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users" element={<Users />} />
<Route path="/users/:id" element={<UserDetail />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
39. How do you implement routing in React?
Answer: Use React Router with BrowserRouter, Routes, Route, and navigation components.
Complete Example:
import { BrowserRouter, Routes, Route, Link, useParams, useNavigate } from 'react-router-dom';
// Navigation component
const Navigation = () => (
<nav>
<Link to="/">Home</Link>
<Link to="/products">Products</Link>
<Link to="/cart">Cart</Link>
</nav>
);
// Home component
const Home = () => <h1>Welcome to our store!</h1>;
// Products component
const Products = () => (
<div>
<h2>Products</h2>
<Link to="/products/1">Product 1</Link>
<Link to="/products/2">Product 2</Link>
</div>
);
// Product detail component
const ProductDetail = () => {
const { id } = useParams();
const navigate = useNavigate();
return (
<div>
<h3>Product {id}</h3>
<button onClick={() => navigate('/products')}>
Back to Products
</button>
</div>
);
};
// Cart component
const Cart = () => <h2>Shopping Cart</h2>;
// Main App
const App = () => (
<BrowserRouter>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="/cart" element={<Cart />} />
</Routes>
</BrowserRouter>
);
40. What are route parameters?
Answer: Route parameters are dynamic segments in URLs that can change based on user interaction. They're defined with :paramName syntax and accessed using useParams hook.
Example:
import { Routes, Route, useParams, Link } from 'react-router-dom';
// Route with parameters
const App = () => (
<Routes>
<Route path="/users/:userId" element={<UserProfile />} />
<Route path="/posts/:postId/comments/:commentId" element={<CommentDetail />} />
<Route path="/category/:categoryId/product/:productId" element={<ProductDetail />} />
</Routes>
);
// Accessing single parameter
const UserProfile = () => {
const { userId } = useParams();
return (
<div>
<h2>User Profile</h2>
<p>User ID: {userId}</p>
<Link to={`/users/${userId}/posts`}>View Posts</Link>
</div>
);
};
// Accessing multiple parameters
const CommentDetail = () => {
const { postId, commentId } = useParams();
return (
<div>
<h3>Comment Details</h3>
<p>Post ID: {postId}</p>
<p>Comment ID: {commentId}</p>
</div>
);
};
// Optional parameters
const ProductDetail = () => {
const { categoryId, productId } = useParams();
return (
<div>
<h3>Product Details</h3>
<p>Category: {categoryId}</p>
<p>Product: {productId}</p>
</div>
);
};
// Nested routes with parameters
const UserPosts = () => {
const { userId } = useParams();
return (
<div>
<h3>Posts by User {userId}</h3>
<Routes>
<Route path="/" element={<PostList userId={userId} />} />
<Route path="/:postId" element={<PostDetail />} />
</Routes>
</div>
);
};
41. How do you handle 404 pages in React Router?
A route with path="*" can render the not-found UI. Modern React Router ranks route matches, so putting the wildcard last is a readability convention rather than an ordering requirement. Client rendering alone does not set an HTTP 404 status; configure the server/framework response when that status matters.
42. What is code splitting in React?
Answer: Code splitting is a technique that allows you to split your JavaScript bundle into smaller chunks that can be loaded on demand. This improves initial load time and reduces the bundle size by only loading the code that's needed for the current page.
Coding Examples:
1. Route-based Code Splitting:
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
// Lazy load components
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</Suspense>
);
}
2. Component-based Code Splitting:
// Heavy component that's not immediately needed
const HeavyChart = lazy(() => import('./components/HeavyChart'));
const Dashboard = () => {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>
Load Chart
</button>
{showChart && (
<Suspense fallback={<div>Loading chart...</div>}>
<HeavyChart />
</Suspense>
)}
</div>
);
};
3. Dynamic Import with Error Handling:
const loadComponent = async (componentName) => {
try {
const module = await import(`./components/${componentName}`);
return module.default;
} catch (error) {
console.error(`Failed to load component: ${componentName}`, error);
return () => <div>Component not found</div>;
}
};
const DynamicComponent = ({ componentName }) => {
const [Component, setComponent] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadComponent(componentName)
.then(comp => setComponent(() => comp))
.finally(() => setLoading(false));
}, [componentName]);
if (loading) return <div>Loading...</div>;
return Component ? <Component /> : <div>Component not found</div>;
};
43. How do you implement lazy loading in React?
Answer: Lazy loading in React is implemented using the lazy() function and Suspense component. It allows you to load components only when they're needed, improving performance.
Coding Examples:
1. Basic Lazy Loading:
import { lazy, Suspense } from 'react';
// Lazy load a component
const ExpensiveComponent = lazy(() => import('./ExpensiveComponent'));
const App = () => (
<Suspense fallback={<div>Loading...</div>}>
<ExpensiveComponent />
</Suspense>
);
2. Lazy Loading with Custom Loading Component:
const LoadingSpinner = () => (
<div className="loading-spinner">
<div className="spinner"></div>
<p>Loading component...</p>
</div>
);
const UserProfile = lazy(() => import('./UserProfile'));
const Settings = lazy(() => import('./Settings'));
const App = () => (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/profile" element={<UserProfile />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
3. Conditional Lazy Loading:
const ConditionalLazyComponent = ({ shouldLoad, componentName }) => {
const [Component, setComponent] = useState(null);
const [loading, setLoading] = useState(false);
const loadComponent = async () => {
setLoading(true);
try {
const module = await import(`./components/${componentName}`);
setComponent(() => module.default);
} catch (error) {
console.error('Failed to load component:', error);
} finally {
setLoading(false);
}
};
useEffect(() => {
if (shouldLoad && !Component) {
loadComponent();
}
}, [shouldLoad, Component, componentName]);
if (loading) return <div>Loading...</div>;
if (!shouldLoad) return null;
if (!Component) return <div>Component not available</div>;
return <Component />;
};
44. What is Suspense in React?
Suspense displays a fallback while a child suspends using a supported integration, such as React.lazy or a framework’s Suspense-enabled data loading. Ordinary fetch calls in useEffect do not activate Suspense. Handle their loading and error state explicitly unless using a supported resource integration.
45. What is error boundary in React?
An error boundary catches errors thrown during rendering and supported lifecycle work in its descendant tree. It does not catch ordinary event-handler errors, arbitrary asynchronous callbacks, server rendering errors, or errors in the boundary itself. Catch asynchronous request failures where they occur and surface an error state. A boundary typically defines getDerivedStateFromError and componentDidCatch in a class.
46. How do you handle errors in React?
Answer: Error handling in React involves multiple strategies: error boundaries for component errors, try-catch for JavaScript errors, and proper error states for async operations.
Coding Examples:
1. Async Error Handling with Custom Hook:
const useAsync = (asyncFunction) => {
const [state, setState] = useState({
data: null,
loading: false,
error: null
});
const execute = useCallback(async (...args) => {
setState({ data: null, loading: true, error: null });
try {
const data = await asyncFunction(...args);
setState({ data, loading: false, error: null });
} catch (error) {
setState({ data: null, loading: false, error });
}
}, [asyncFunction]);
return { ...state, execute };
};
// Usage
const UserProfile = () => {
const { data: user, loading, error, execute: fetchUser } = useAsync(fetchUserData);
useEffect(() => {
fetchUser(123);
}, [fetchUser]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!user) return <div>No user data</div>;
return <div>User: {user.name}</div>;
};
2. Form Error Handling:
const useForm = (initialValues) => {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setValues(prev => ({ ...prev, [name]: value }));
// Clear error when user starts typing
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: '' }));
}
};
const handleBlur = (e) => {
const { name } = e.target;
setTouched(prev => ({ ...prev, [name]: true }));
};
const validate = () => {
const newErrors = {};
if (!values.email) {
newErrors.email = 'Email is required';
} else if (!/\S+@\S+\.\S+/.test(values.email)) {
newErrors.email = 'Email is invalid';
}
if (!values.password) {
newErrors.password = 'Password is required';
} else if (values.password.length < 6) {
newErrors.password = 'Password must be at least 6 characters';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
return { values, errors, touched, handleChange, handleBlur, validate };
};
const LoginForm = () => {
const { values, errors, touched, handleChange, handleBlur, validate } = useForm({
email: '',
password: ''
});
const handleSubmit = async (e) => {
e.preventDefault();
if (!validate()) return;
try {
await loginUser(values);
} catch (error) {
console.error('Login failed:', error);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="email"
name="email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
placeholder="Email"
/>
{touched.email && errors.email && (
<span className="error">{errors.email}</span>
)}
</div>
<div>
<input
type="password"
name="password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
placeholder="Password"
/>
{touched.password && errors.password && (
<span className="error">{errors.password}</span>
)}
</div>
<button type="submit">Login</button>
</form>
);
};
47. What is server-side rendering (SSR)?
Answer: Server-side rendering is a technique where React components are rendered on the server and sent to the client as HTML. This improves initial page load performance, SEO, and provides better user experience.
Coding Examples:
1. Basic SSR Implementation:
// Server-side rendering with Express
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './App';
const app = express();
app.get('*', (req, res) => {
const html = ReactDOMServer.renderToString(<App />);
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>My SSR App</title>
</head>
<body>
<div id="root">${html}</div>
<script src="/client.js"></script>
</body>
</html>
`);
});
app.listen(3000);
2. SSR with Data Fetching:
// Server component with data fetching
const UserProfile = ({ user }) => (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
// Server-side data fetching
const fetchUserData = async (userId) => {
const response = await fetch(`https://api.example.com/users/${userId}`);
return response.json();
};
// Server route
app.get('/user/:id', async (req, res) => {
try {
const user = await fetchUserData(req.params.id);
const html = ReactDOMServer.renderToString(
<UserProfile user={user} />
);
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>${user.name} - Profile</title>
</head>
<body>
<div id="root">${html}</div>
<script>
window.__INITIAL_DATA__ = ${JSON.stringify(user)};
</script>
<script src="/client.js"></script>
</body>
</html>
`);
} catch (error) {
res.status(500).send('Error loading user');
}
});
48. What is hydration in React?
Answer: Hydration is the process where React attaches event listeners to the server-rendered HTML and makes it interactive. It matches the server-rendered markup with the client-side React tree.
Coding Examples:
1. Basic Hydration:
// Server-side rendering
const serverHtml = ReactDOMServer.renderToString(<App />);
// Client-side hydration
ReactDOM.hydrate(<App />, document.getElementById('root'));
2. Hydration with Data:
// Server sends initial data
const serverData = { user: { name: 'John', email: 'john@example.com' } };
// Server renders with data
const serverHtml = ReactDOMServer.renderToString(
<App initialData={serverData} />
);
// Client receives the same data and hydrates
const clientData = window.__INITIAL_DATA__;
const App = ({ initialData }) => {
const [data, setData] = useState(initialData);
return (
<div>
<h1>Welcome, {data.user.name}!</h1>
<button onClick={() => setData({ ...data, count: (data.count || 0) + 1 })}>
Count: {data.count || 0}
</button>
</div>
);
};
// Hydrate with the same initial data
ReactDOM.hydrate(
<App initialData={clientData} />,
document.getElementById('root')
);
3. Hydration Mismatch Handling:
// Component that might have hydration mismatches
const TimeComponent = () => {
const [time, setTime] = useState(null);
useEffect(() => {
// Only set time on client to avoid hydration mismatch
setTime(new Date().toLocaleTimeString());
}, []);
// Show loading state during hydration
if (time === null) {
return <div>Loading time...</div>;
}
return <div>Current time: {time}</div>;
};
// Suppress hydration warnings when needed
const SuppressHydrationWarning = ({ children }) => (
<div suppressHydrationWarning={true}>
{children}
</div>
);
49. What is Next.js and how does it relate to React?
Answer: Next.js is a React framework that provides features like server-side rendering, static site generation, routing, and API routes out of the box. It builds on top of React to provide a complete development framework.
Coding Examples:
1. Basic Next.js App:
// pages/index.js
export default function Home() {
return (
<div>
<h1>Welcome to Next.js!</h1>
<p>This is a server-side rendered page</p>
</div>
);
}
// pages/about.js
export default function About() {
return (
<div>
<h1>About Us</h1>
<p>This is the about page</p>
</div>
);
}
2. Next.js with Data Fetching:
// pages/users/[id].js
export default function UserProfile({ user }) {
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
// Server-side data fetching
export async function getServerSideProps({ params }) {
try {
const response = await fetch(`https://api.example.com/users/${params.id}`);
const user = await response.json();
return {
props: { user }
};
} catch (error) {
return {
notFound: true
};
}
}
// Static generation with data
export async function getStaticProps() {
const response = await fetch('https://api.example.com/posts');
const posts = await response.json();
return {
props: { posts },
revalidate: 60 // Regenerate every 60 seconds
};
}
3. Next.js API Routes:
// pages/api/users.js
export default function handler(req, res) {
if (req.method === 'GET') {
// Handle GET request
res.status(200).json({ users: [] });
} else if (req.method === 'POST') {
// Handle POST request
const { name, email } = req.body;
res.status(201).json({ message: 'User created', user: { name, email } });
} else {
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
// pages/api/users/[id].js
export default function handler(req, res) {
const { id } = req.query;
if (req.method === 'GET') {
res.status(200).json({ id, name: 'John Doe', email: 'john@example.com' });
} else if (req.method === 'PUT') {
res.status(200).json({ message: 'User updated', id });
} else if (req.method === 'DELETE') {
res.status(200).json({ message: 'User deleted', id });
}
}
50. What is the difference between SSR and client-side rendering?
Answer: The main differences are:
SSR (Server-Side Rendering): - HTML is generated on the server - Faster initial page load - Better SEO - Works without JavaScript - Higher server load
CSR (Client-Side Rendering): - HTML is generated in the browser - Slower initial load (needs to download JS first) - Poorer SEO - Requires JavaScript - Lower server load
Coding Examples:
1. SSR Implementation:
// Server-side rendering
const express = require('express');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const app = express();
app.get('/', (req, res) => {
const html = ReactDOMServer.renderToString(
<div>
<h1>Server Rendered Content</h1>
<p>This content is visible immediately</p>
</div>
);
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>SSR App</title>
</head>
<body>
<div id="root">${html}</div>
<script src="/client.js"></script>
</body>
</html>
`);
});
2. CSR Implementation:
// Client-side rendering
import React from 'react';
import ReactDOM from 'react-dom';
const App = () => (
<div>
<h1>Client Rendered Content</h1>
<p>This content loads after JavaScript executes</p>
</div>
);
ReactDOM.render(<App />, document.getElementById('root'));
3. Hybrid Approach:
// Next.js hybrid rendering
// pages/index.js - Static generation
export default function Home({ posts }) {
return (
<div>
<h1>Static Content</h1>
{posts.map(post => (
<div key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</div>
))}
</div>
);
}
export async function getStaticProps() {
const posts = await fetchPosts();
return {
props: { posts },
revalidate: 3600 // Regenerate every hour
};
}
// pages/dashboard.js - Client-side only
import dynamic from 'next/dynamic';
const Dashboard = dynamic(() => import('../components/Dashboard'), {
ssr: false // Disable SSR for this component
});
export default function DashboardPage() {
return <Dashboard />;
}
These examples demonstrate the key concepts and implementations for each topic, providing comprehensive answers suitable for a technical lead interview.
51. What is Static Site Generation (SSG)?
Answer: Static Site Generation is a technique where HTML pages are pre-built at build time rather than being generated on each request. The pages are created once and served as static files, making them extremely fast to load.
Key Benefits: - Performance: Pages load instantly since they're pre-built - SEO: Search engines can easily crawl static HTML - Security: Reduced attack surface with no server-side code execution - Scalability: Can be served from CDN globally - Cost: Lower hosting costs
Coding Example (Next.js):
// pages/blog/[slug].js
export async function getStaticPaths() {
const posts = await fetch('https://api.example.com/posts');
const paths = posts.map((post) => ({
params: { slug: post.slug },
}));
return {
paths,
fallback: false, // 404 for non-existent paths
};
}
export async function getStaticProps({ params }) {
const post = await fetch(`https://api.example.com/posts/${params.slug}`);
return {
props: {
post,
},
revalidate: 3600, // Regenerate every hour (ISR)
};
}
function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
52. How do you fetch data in React?
Answer: There are several ways to fetch data in React, each with different use cases and trade-offs.
1. Using useEffect (Functional Components):
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async () => {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('Failed to fetch user');
}
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>No user found</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
2. Using React Query (Recommended for complex data fetching):
import { useQuery } from '@tanstack/react-query';
function UserProfile({ userId }) {
const { data: user, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetch(`/api/users/${userId}`).then(res => res.json()),
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 10 * 60 * 1000, // 10 minutes
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
3. Using SWR:
import useSWR from 'swr';
const fetcher = (url) => fetch(url).then((res) => res.json());
function UserProfile({ userId }) {
const { data: user, error, isLoading } = useSWR(
`/api/users/${userId}`,
fetcher,
{
refreshInterval: 30000, // Refresh every 30 seconds
revalidateOnFocus: true,
}
);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
53. What is the difference between useEffect and componentDidMount?
componentDidMount runs after a class component is mounted. useEffect describes synchronization with dependencies, and can clean up/re-run as those dependencies change. They are not exact substitutes; effects should be designed around synchronization rather than lifecycle imitation. useEffect synchronizes a committed component with an external system. List every reactive dependency; cleanup runs before the effect is re-established after dependency changes and on unmount. Strict Mode adds a development-only setup/cleanup check. An empty dependency array does not promise a single execution over the application lifetime. Effects do not run during server rendering. Reference: useEffect.
54. What is the difference between componentWillMount and componentDidMount?
Answer: These are lifecycle methods in class components with different timing and use cases.
componentWillMount (Deprecated):
class UserProfile extends React.Component {
constructor(props) {
super(props);
this.state = {
user: null
};
}
componentWillMount() {
// Runs before render - DEPRECATED
// Avoid side effects here
console.log('Component will mount');
}
componentDidMount() {
// Runs after render - SAFE for side effects
this.fetchUser();
}
async fetchUser() {
const response = await fetch(`/api/users/${this.props.userId}`);
const user = await response.json();
this.setState({ user });
}
render() {
return <div>{this.state.user?.name || 'Loading...'}</div>;
}
}
Key Differences:
- componentWillMount: Runs before render (deprecated in React 16.3)
- componentDidMount: Runs after render and DOM insertion
- Side effects should be in componentDidMount, not componentWillMount
- componentWillMount can cause issues with server-side rendering
55. What is the difference between componentDidUpdate and useEffect?
Answer: Both handle updates but with different patterns and capabilities.
componentDidUpdate (Class Component):
class UserProfile extends React.Component {
state = {
user: null,
posts: []
};
componentDidMount() {
this.fetchUser();
}
componentDidUpdate(prevProps, prevState) {
// Runs after every update
if (prevProps.userId !== this.props.userId) {
this.fetchUser();
}
if (prevState.user?.id !== this.state.user?.id && this.state.user) {
this.fetchPosts();
}
}
async fetchUser() {
const response = await fetch(`/api/users/${this.props.userId}`);
const user = await response.json();
this.setState({ user });
}
async fetchPosts() {
const response = await fetch(`/api/users/${this.state.user.id}/posts`);
const posts = await response.json();
this.setState({ posts });
}
render() {
return (
<div>
<h1>{this.state.user?.name}</h1>
<div>{this.state.posts.length} posts</div>
</div>
);
}
}
useEffect (Functional Component):
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
// Runs when userId changes (like componentDidUpdate)
useEffect(() => {
const fetchUser = async () => {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
setUser(userData);
};
fetchUser();
}, [userId]); // Dependency array controls when effect runs
// Runs when user changes
useEffect(() => {
if (!user) return;
const fetchPosts = async () => {
const response = await fetch(`/api/users/${user.id}/posts`);
const postsData = await response.json();
setPosts(postsData);
};
fetchPosts();
}, [user?.id]); // Only when user.id changes
return (
<div>
<h1>{user?.name}</h1>
<div>{posts.length} posts</div>
</div>
);
}
Key Differences:
- componentDidUpdate runs after every update
- useEffect with dependencies runs only when dependencies change
- useEffect can be split into multiple effects for different concerns
- useEffect has better control over when effects run
56. What is the difference between componentWillUnmount and useEffect cleanup?
componentWillUnmount runs when a class component is removed. Effect cleanup also runs before a dependency-driven effect rerun, and during the extra development Strict Mode check. Cleanup should undo the corresponding setup. Reference: useEffect.
57. What are Higher-Order Components (HOC)?
Answer: HOCs are functions that take a component and return a new component with additional props or behavior.
Basic HOC Pattern:
// HOC that adds loading state
const withLoading = (WrappedComponent) => {
return function WithLoadingComponent(props) {
const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch('/api/data');
const result = await response.json();
setData(result);
} catch (error) {
console.error('Error fetching data:', error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return (
<WrappedComponent
{...props}
loading={loading}
data={data}
refetch={fetchData}
/>
);
};
};
// Usage
const UserList = ({ loading, data, refetch }) => {
if (loading) return <div>Loading...</div>;
return (
<div>
<button onClick={refetch}>Refresh</button>
{data?.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
};
const UserListWithLoading = withLoading(UserList);
Advanced HOC with Error Handling:
const withErrorBoundary = (WrappedComponent) => {
return class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by HOC:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div className="error-boundary">
<h2>Something went wrong</h2>
<button onClick={() => this.setState({ hasError: false })}>
Try again
</button>
</div>
);
}
return <WrappedComponent {...this.props} />;
}
};
};
// HOC that adds authentication
const withAuth = (WrappedComponent) => {
return function AuthenticatedComponent(props) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Check authentication status
checkAuth().then(user => {
setUser(user);
setLoading(false);
});
}, []);
if (loading) return <div>Checking authentication...</div>;
if (!user) return <div>Please log in</div>;
return <WrappedComponent {...props} user={user} />;
};
};
// Composing multiple HOCs
const EnhancedComponent = withErrorBoundary(
withAuth(
withLoading(UserList)
)
);
58. What are Render Props?
Answer: Render props is a pattern where a component receives a function as a prop and calls it to render content.
Basic Render Props Pattern:
// Component that provides data via render prop
class DataFetcher extends React.Component {
state = {
data: null,
loading: false,
error: null
};
async fetchData() {
this.setState({ loading: true, error: null });
try {
const response = await fetch('/api/data');
const data = await response.json();
this.setState({ data, loading: false });
} catch (error) {
this.setState({ error: error.message, loading: false });
}
}
componentDidMount() {
this.fetchData();
}
render() {
return this.props.render(this.state);
}
}
// Usage
function App() {
return (
<DataFetcher
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 (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}}
/>
);
}
Advanced Render Props with Children Function:
// Mouse tracker with render props
class MouseTracker extends React.Component {
state = {
x: 0,
y: 0
};
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
render() {
return (
<div onMouseMove={this.handleMouseMove}>
{this.props.children(this.state)}
</div>
);
}
}
// Usage
function App() {
return (
<MouseTracker>
{({ x, y }) => (
<div>
<h1>Mouse position: ({x}, {y})</h1>
<div
style={{
position: 'absolute',
left: x,
top: y,
width: 20,
height: 20,
backgroundColor: 'red',
borderRadius: '50%',
pointerEvents: 'none'
}}
/>
</div>
)}
</MouseTracker>
);
}
Functional Component with Render Props:
function useDataFetcher(url) {
const [state, setState] = useState({
data: null,
loading: true,
error: null
});
useEffect(() => {
const fetchData = async () => {
try {
setState(prev => ({ ...prev, loading: true }));
const response = await fetch(url);
const data = await response.json();
setState({ data, loading: false, error: null });
} catch (error) {
setState({ data: null, loading: false, error: error.message });
}
};
fetchData();
}, [url]);
return state;
}
function DataFetcher({ url, render }) {
const state = useDataFetcher(url);
return render(state);
}
// Usage
function App() {
return (
<DataFetcher
url="/api/users"
render={({ data, loading, error }) => {
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
{data?.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}}
/>
);
}
59. What is the difference between HOC and Render Props?
Answer: Both patterns share logic between components but have different approaches and trade-offs.
HOC Approach:
// HOC that adds mouse tracking
const withMouseTracking = (WrappedComponent) => {
return class MouseTracker extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
render() {
return (
<div onMouseMove={this.handleMouseMove}>
<WrappedComponent
{...this.props}
mouseX={this.state.x}
mouseY={this.state.y}
/>
</div>
);
}
};
};
// Usage
const MouseDisplay = ({ mouseX, mouseY }) => (
<div>Mouse: ({mouseX}, {mouseY})</div>
);
const EnhancedMouseDisplay = withMouseTracking(MouseDisplay);
Render Props Approach:
// Render props component for mouse tracking
class MouseTracker extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
render() {
return (
<div onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
// Usage
function App() {
return (
<MouseTracker
render={({ x, y }) => (
<div>Mouse: ({x}, {y})</div>
)}
/>
);
}
Key Differences:
- Prop Naming Conflicts:
// HOC - potential naming conflicts
const withUser = (Component) => (props) => (
<Component {...props} user={getUser()} />
);
const withTheme = (Component) => (props) => (
<Component {...props} theme={getTheme()} />
);
// Both might add 'user' prop, causing conflicts
const MyComponent = withUser(withTheme(Component));
// Render props - no naming conflicts
function App() {
return (
<UserProvider render={({ user }) => (
<ThemeProvider render={({ theme }) => (
<Component user={user} theme={theme} />
)} />
)} />
);
}
- Dynamic Composition:
// HOC - static composition
const EnhancedComponent = withAuth(withLoading(Component));
// Render props - dynamic composition
function App({ isAuthenticated }) {
return (
<div>
{isAuthenticated ? (
<AuthProvider render={({ user }) => (
<LoadingProvider render={({ loading, data }) => (
<Component user={user} loading={loading} data={data} />
)} />
)} />
) : (
<LoginComponent />
)}
</div>
);
}
60. What is React Fiber?
Answer: React Fiber is the new reconciliation algorithm introduced in React 16 that enables incremental rendering and better performance.
Key Concepts:
- Fiber Nodes:
// Simplified representation of a Fiber node
type Fiber = {
// Tag identifying the type of fiber
tag: WorkTag,
// Unique identifier of this child
key: null | string,
// The resolved function/class/ associated with this fiber
elementType: any,
// The local state associated with this fiber
memoizedState: any,
// The props used to create the output
memoizedProps: any,
// The state used to create the output
pendingProps: any,
// A queue of state updates and callbacks
updateQueue: mixed,
// The ref returned from the render function
ref: null | (((handle: mixed) => void) & { _stringRef: ?string }) | RefObject,
// Input is the data coming into process this fiber
pendingProps: any,
// The output of the fiber
memoizedProps: any,
// The state used to create the output
memoizedState: any,
// Dependencies (contexts, events) for this fiber
dependencies: Dependencies | null,
// Singly Linked List Tree Structure
child: Fiber | null,
sibling: Fiber | null,
return: Fiber | null,
// Time tracking
actualStartTime: number,
actualDuration: number,
// Priority
lanes: Lanes,
childLanes: Lanes,
};
- Incremental Rendering:
// React can now pause and resume work
function ExpensiveComponent({ items }) {
return (
<div>
{items.map((item, index) => (
<ExpensiveItem
key={item.id}
item={item}
// React can prioritize rendering based on priority
priority={index < 10 ? 'high' : 'low'}
/>
))}
</div>
);
}
// React Fiber enables better error boundaries
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Fiber enables better error recovery
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Fiber provides better error information
console.log('Error caught by boundary:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
- Concurrent Features:
// React 18+ concurrent features enabled by Fiber
import { startTransition, useTransition } from 'react';
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleSearch = (newQuery) => {
setQuery(newQuery); // Immediate update
// Mark this as a transition (non-urgent)
startTransition(() => {
// This update can be interrupted
setResults(expensiveSearch(newQuery));
});
};
return (
<div>
<input
value={query}
onChange={(e) => handleSearch(e.target.value)}
/>
{isPending && <div>Searching...</div>}
<SearchResults results={results} />
</div>
);
}
// Suspense for data fetching
function UserProfile({ userId }) {
return (
<Suspense fallback={<div>Loading profile...</div>}>
<UserDetails userId={userId} />
</Suspense>
);
}
// Suspense for code splitting
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
Benefits of React Fiber:
- Better Performance:
// Fiber enables better scheduling
function App() {
const [count, setCount] = useState(0);
const [heavyData, setHeavyData] = useState(null);
const handleClick = () => {
setCount(c => c + 1); // High priority update
// Low priority update that can be interrupted
startTransition(() => {
setHeavyData(expensiveCalculation());
});
};
return (
<div>
<button onClick={handleClick}>
Count: {count}
</button>
{heavyData && <HeavyComponent data={heavyData} />}
</div>
);
}
- Better Error Handling:
// Fiber provides better error recovery
function ComponentWithError() {
const [shouldError, setShouldError] = useState(false);
if (shouldError) {
throw new Error('Simulated error');
}
return (
<div>
<button onClick={() => setShouldError(true)}>
Trigger Error
</button>
</div>
);
}
// Error boundary can recover better with Fiber
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Fiber provides more detailed error information
console.log('Component stack:', errorInfo.componentStack);
}
render() {
if (this.state.hasError) {
return (
<div>
<h1>Something went wrong.</h1>
<button onClick={() => this.setState({ hasError: false })}>
Try again
</button>
</div>
);
}
return this.props.children;
}
}
These answers demonstrate deep understanding of React concepts and patterns, which is essential for a technical lead position. The examples show practical implementation knowledge and awareness of modern React patterns and best practices.
61. What is reconciliation algorithm?
Answer: The reconciliation algorithm is React's diffing algorithm that determines what changes need to be made to the DOM when component state or props change. It's the core mechanism behind React's virtual DOM.
Key Concepts: - Virtual DOM Comparison: React creates a virtual representation of the UI and compares it with the previous version - Diffing Process: Identifies the minimal set of changes needed - Batch Updates: Groups multiple changes for efficient DOM updates
Coding Example:
// React's reconciliation process in action
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li> // Key helps reconciliation
))}
</ul>
);
}
// When users array changes, React will:
// 1. Compare old virtual DOM with new virtual DOM
// 2. Identify which <li> elements changed
// 3. Apply only necessary DOM updates
62. What is the difference between React and Angular?
Angular is an integrated application framework with templates, DI, routing, forms, and HTTP support. React is a UI library with components, state, and context, often used with an application framework. Both support TypeScript. Learning curve, performance, and total bundle size depend on the application; avoid fixed rankings.
63. What is the difference between React and Vue?
Answer:
| Aspect | React | Vue |
|---|---|---|
| Template Syntax | JSX | HTML with directives |
| Learning Curve | Steeper | Gentler |
| Ecosystem | Larger, more fragmented | Smaller, more cohesive |
| Performance | Virtual DOM | Virtual DOM + optimizations |
| Mobile | React Native | NativeScript-Vue |
Coding Examples:
React Component:
import React, { useState } from 'react';
const TodoList = () => {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
setTodos([...todos, { id: Date.now(), text: input }]);
setInput('');
};
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
/>
<button onClick={addTodo}>Add Todo</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</div>
);
};
Vue Component:
<template>
<div>
<input v-model="input" />
<button @click="addTodo">Add Todo</button>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
todos: [],
input: ''
};
},
methods: {
addTodo() {
this.todos.push({ id: Date.now(), text: this.input });
this.input = '';
}
}
};
</script>
64. What is the role of keys in lists?
Answer: Keys help React identify which items have changed, been added, or been removed in lists. They provide a stable identity to elements across re-renders.
Key Benefits: - Performance: Enables efficient updates - State Preservation: Maintains component state across re-renders - Animation: Helps with smooth transitions
Coding Examples:
Without Keys (Problematic):
// ❌ Bad - React can't efficiently track items
const TodoList = ({ todos }) => (
<ul>
{todos.map(todo => (
<li>{todo.text}</li> // No key - React uses index by default
))}
</ul>
);
With Keys (Recommended):
// ✅ Good - React can efficiently track items
const TodoList = ({ todos }) => (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li> // Unique, stable key
))}
</ul>
);
// ✅ Alternative with index (only if list is static)
const StaticList = ({ items }) => (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li> // Only for static lists
))}
</ul>
);
Complex Example with State:
const TodoItem = ({ todo, onToggle }) => {
const [isEditing, setIsEditing] = useState(false);
return (
<li>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
{isEditing ? (
<input defaultValue={todo.text} />
) : (
<span>{todo.text}</span>
)}
</li>
);
};
// Keys preserve the editing state when list order changes
const TodoList = ({ todos, onToggle }) => (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id} // Preserves component state
todo={todo}
onToggle={onToggle}
/>
))}
</ul>
);
65. How do you pass data from child to parent component?
Answer: Data flows from child to parent through callback functions passed as props.
Coding Examples:
Basic Example:
// Parent Component
const Parent = () => {
const [childData, setChildData] = useState('');
const handleChildData = (data) => {
setChildData(data);
};
return (
<div>
<h2>Parent received: {childData}</h2>
<Child onDataSend={handleChildData} />
</div>
);
};
// Child Component
const Child = ({ onDataSend }) => {
const handleClick = () => {
onDataSend('Hello from child!');
};
return <button onClick={handleClick}>Send Data to Parent</button>;
};
Form Example:
// Parent Component
const UserForm = () => {
const [userData, setUserData] = useState(null);
const handleFormSubmit = (formData) => {
setUserData(formData);
// Process form data
console.log('Form submitted:', formData);
};
return (
<div>
{userData && <p>Welcome, {userData.name}!</p>}
<UserFormFields onSubmit={handleFormSubmit} />
</div>
);
};
// Child Component
const UserFormFields = ({ onSubmit }) => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
onSubmit({ name, email });
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit">Submit</button>
</form>
);
};
66. How do you pass data between sibling components?
Answer: Sibling components communicate through a common parent component (lifting state up) or using external state management solutions.
Coding Examples:
Lifting State Up:
// Parent Component (mediator)
const Parent = () => {
const [sharedData, setSharedData] = useState('');
return (
<div>
<SiblingA onDataChange={setSharedData} />
<SiblingB data={sharedData} />
</div>
);
};
// Sibling A (sender)
const SiblingA = ({ onDataChange }) => {
const handleClick = () => {
onDataChange('Data from Sibling A');
};
return <button onClick={handleClick}>Send to Sibling B</button>;
};
// Sibling B (receiver)
const SiblingB = ({ data }) => {
return <div>Received: {data}</div>;
};
Using Context API:
// Create Context
const DataContext = createContext();
// Provider Component
const DataProvider = ({ children }) => {
const [sharedData, setSharedData] = useState('');
return (
<DataContext.Provider value={{ sharedData, setSharedData }}>
{children}
</DataContext.Provider>
);
};
// Sibling A
const SiblingA = () => {
const { setSharedData } = useContext(DataContext);
return (
<button onClick={() => setSharedData('New data')}>
Update Shared Data
</button>
);
};
// Sibling B
const SiblingB = () => {
const { sharedData } = useContext(DataContext);
return <div>Shared Data: {sharedData}</div>;
};
// Usage
const App = () => (
<DataProvider>
<SiblingA />
<SiblingB />
</DataProvider>
);
Using Custom Hook:
// Custom Hook for shared state
const useSharedState = () => {
const [state, setState] = useState('');
const updateState = useCallback((newValue) => {
setState(newValue);
}, []);
return { state, updateState };
};
// Parent with hook
const Parent = () => {
const sharedState = useSharedState();
return (
<div>
<SiblingA {...sharedState} />
<SiblingB {...sharedState} />
</div>
);
};
67. What is lifting state up?
Answer: Lifting state up is moving state from a child component to its parent component so that multiple children can share the same state.
Benefits: - Shared State: Multiple components can access the same data - Single Source of Truth: State is managed in one place - Predictable Updates: Changes flow down through props
Coding Examples:
Before Lifting State:
// ❌ Each component manages its own state
const TemperatureInput = () => {
const [temperature, setTemperature] = useState('');
return (
<input
value={temperature}
onChange={(e) => setTemperature(e.target.value)}
/>
);
};
const TemperatureDisplay = () => {
const [temperature, setTemperature] = useState('');
return <div>Temperature: {temperature}°C</div>;
};
After Lifting State:
// ✅ State lifted to parent
const TemperatureConverter = () => {
const [temperature, setTemperature] = useState('');
const [scale, setScale] = useState('celsius');
const celsius = scale === 'fahrenheit'
? tryConvert(temperature, toCelsius)
: temperature;
const fahrenheit = scale === 'celsius'
? tryConvert(temperature, toFahrenheit)
: temperature;
return (
<div>
<TemperatureInput
scale="celsius"
temperature={celsius}
onTemperatureChange={setTemperature}
onScaleChange={setScale}
/>
<TemperatureInput
scale="fahrenheit"
temperature={fahrenheit}
onTemperatureChange={setTemperature}
onScaleChange={setScale}
/>
<BoilingVerdict celsius={parseFloat(celsius)} />
</div>
);
};
const TemperatureInput = ({ scale, temperature, onTemperatureChange, onScaleChange }) => {
const handleChange = (e) => {
onScaleChange(scale);
onTemperatureChange(e.target.value);
};
return (
<fieldset>
<legend>Enter temperature in {scale}:</legend>
<input value={temperature} onChange={handleChange} />
</fieldset>
);
};
68. What is the use of fragments in React?
Answer: Fragments allow you to group multiple elements without adding extra DOM nodes.
Benefits: - Cleaner DOM: No unnecessary wrapper elements - Better Performance: Fewer DOM nodes - CSS Compatibility: Avoids layout issues with flexbox/grid
Coding Examples:
Without Fragments (Problematic):
// ❌ Extra div wrapper
const UserProfile = () => {
return (
<div> {/* Unnecessary wrapper */}
<h1>User Profile</h1>
<p>Name: John Doe</p>
<p>Email: john@example.com</p>
</div>
);
};
With Fragments:
// ✅ Clean structure
const UserProfile = () => {
return (
<React.Fragment>
<h1>User Profile</h1>
<p>Name: John Doe</p>
<p>Email: john@example.com</p>
</React.Fragment>
);
};
// ✅ Shorthand syntax
const UserProfile = () => {
return (
<>
<h1>User Profile</h1>
<p>Name: John Doe</p>
<p>Email: john@example.com</p>
</>
);
};
With Keys (Only React.Fragment supports keys):
const UserList = ({ users }) => {
return (
<div>
{users.map(user => (
<React.Fragment key={user.id}>
<h3>{user.name}</h3>
<p>{user.email}</p>
<hr />
</React.Fragment>
))}
</div>
);
};
Conditional Rendering:
const ConditionalContent = ({ showHeader, showFooter }) => {
return (
<>
{showHeader && <header>Header Content</header>}
<main>Main Content</main>
{showFooter && <footer>Footer Content</footer>}
</>
);
};
69. What is the difference between <React.Fragment> and <>?
<>...</> and <Fragment>...</Fragment> group children without a wrapper DOM element. The shorthand cannot carry a key; use <Fragment key={id}> for a keyed group. Import Fragment from react, or import React to use React.Fragment; React is not universally a global. There is no meaningful general bundle-size rule between these forms.
import { Fragment } from 'react';
export function Items({ items }) {
return items.map(item => (
<Fragment key={item.id}><h3>{item.title}</h3><p>{item.description}</p></Fragment>
));
}
70. What is the use of portals in React?
Answer: Portals allow you to render children into a DOM node that exists outside the parent component's DOM hierarchy.
Use Cases: - Modals/Dialogs: Render outside main app container - Tooltips: Render at document body level - Notifications: Avoid z-index issues
Coding Examples:
Basic Modal Portal:
import { createPortal } from 'react-dom';
const Modal = ({ isOpen, onClose, children }) => {
if (!isOpen) return null;
return createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
{children}
<button onClick={onClose}>Close</button>
</div>
</div>,
document.body
);
};
// Usage
const App = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
<h2>Modal Content</h2>
<p>This is rendered in a portal!</p>
</Modal>
</div>
);
};
Tooltip Portal:
const Tooltip = ({ children, content, position }) => {
const [isVisible, setIsVisible] = useState(false);
const [coords, setCoords] = useState({ x: 0, y: 0 });
const handleMouseEnter = (e) => {
const rect = e.target.getBoundingClientRect();
setCoords({ x: rect.left, y: rect.top });
setIsVisible(true);
};
return (
<>
<span onMouseEnter={handleMouseEnter} onMouseLeave={() => setIsVisible(false)}>
{children}
</span>
{isVisible && createPortal(
<div
className="tooltip"
style={{
position: 'fixed',
left: coords.x,
top: coords.y - 30,
zIndex: 1000
}}
>
{content}
</div>,
document.body
)}
</>
);
};
Notification System:
const NotificationPortal = ({ notifications }) => {
return createPortal(
<div className="notification-container">
{notifications.map(notification => (
<div key={notification.id} className="notification">
{notification.message}
</div>
))}
</div>,
document.getElementById('notification-root')
);
};
71. How do you handle animations in React?
Answer: React animations can be handled through CSS transitions, CSS-in-JS libraries, or dedicated animation libraries.
Coding Examples:
CSS Transitions:
const AnimatedComponent = () => {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
Toggle Animation
</button>
<div className={`animated-box ${isVisible ? 'visible' : ''}`}>
Animated Content
</div>
</div>
);
};
// CSS
.animated-box {
opacity: 0;
transform: translateY(20px);
transition: all 0.3s ease-in-out;
}
.animated-box.visible {
opacity: 1;
transform: translateY(0);
}
React Transition Group:
import { CSSTransition, TransitionGroup } from 'react-transition-group';
const TodoList = () => {
const [todos, setTodos] = useState([]);
const addTodo = () => {
setTodos([...todos, { id: Date.now(), text: `Todo ${todos.length + 1}` }]);
};
const removeTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div>
<button onClick={addTodo}>Add Todo</button>
<TransitionGroup>
{todos.map(todo => (
<CSSTransition key={todo.id} timeout={300} classNames="todo">
<div className="todo-item">
{todo.text}
<button onClick={() => removeTodo(todo.id)}>Remove</button>
</div>
</CSSTransition>
))}
</TransitionGroup>
</div>
);
};
// CSS
.todo-enter {
opacity: 0;
transform: translateX(-100%);
}
.todo-enter-active {
opacity: 1;
transform: translateX(0);
transition: all 300ms ease-in;
}
.todo-exit {
opacity: 1;
transform: translateX(0);
}
.todo-exit-active {
opacity: 0;
transform: translateX(100%);
transition: all 300ms ease-out;
}
Framer Motion:
import { motion, AnimatePresence } from 'framer-motion';
const AnimatedModal = ({ isOpen, onClose }) => {
return (
<AnimatePresence>
{isOpen && (
<motion.div
className="modal-overlay"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
>
<motion.div
className="modal-content"
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.8, opacity: 0 }}
onClick={e => e.stopPropagation()}
>
<h2>Animated Modal</h2>
<p>This modal animates smoothly!</p>
</motion.div>
</motion.div>
)}
</AnimatePresence>
);
};
72. What is the use of PropTypes?
In older React versions, propTypes provided development-time runtime checks. React 19 no longer performs these checks through the component propTypes property. Use TypeScript for static prop contracts and explicit runtime schema validation for external data when required. Reference: React 19 upgrade.
73. What is defaultProps?
For function components in React 19+, use default parameters; function defaultProps support was removed. Class components still support defaultProps. Defaults apply to omitted/undefined values, not explicit null. Reference: React 19 upgrade.
export function Greeting({ name = 'Guest' }) {
return <p>Hello, {name}</p>;
}
74. What is the difference between defaultProps and ES6 default parameters?
For function components in React 19+, use default parameters; function defaultProps support was removed. Class components still support defaultProps. Defaults apply to omitted/undefined values, not explicit null. Reference: React 19 upgrade.
export function Greeting({ name = 'Guest' }) {
return <p>Hello, {name}</p>;
}
75. What is the use of shouldComponentUpdate?
Answer: shouldComponentUpdate is a lifecycle method that allows you to control when a component should re-render, optimizing performance by preventing unnecessary updates.
Benefits: - Performance Optimization: Skip re-renders when props/state haven't meaningfully changed - Custom Logic: Implement custom comparison logic - Prevent Cascading Updates: Stop unnecessary child re-renders
Coding Examples:
Basic shouldComponentUpdate:
class UserCard extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// Only re-render if name or age changed
return (
this.props.name !== nextProps.name ||
this.props.age !== nextProps.age
);
}
render() {
const { name, age, email } = this.props;
return (
<div className="user-card">
<h3>{name}</h3>
<p>Age: {age}</p>
<p>Email: {email}</p>
</div>
);
}
}
Deep Comparison:
class ProductList extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// Deep comparison for arrays
if (this.props.products.length !== nextProps.products.length) {
return true;
}
for (let i = 0; i < this.props.products.length; i++) {
const current = this.props.products[i];
const next = nextProps.products[i];
if (
current.id !== next.id ||
current.name !== next.name ||
current.price !== next.price
) {
return true;
}
}
return false;
}
render() {
return (
<div>
{this.props.products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
}
With State Comparison:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0, lastUpdated: Date.now() };
}
shouldComponentUpdate(nextProps, nextState) {
// Only update if count changed (ignore lastUpdated)
return this.state.count !== nextState.count;
}
increment = () => {
this.setState(prevState => ({
count: prevState.count + 1,
lastUpdated: Date.now() // This won't trigger re-render
}));
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
Modern Alternative with React.memo:
// Functional component equivalent
const UserCard = React.memo(({ name, age, email }) => {
return (
<div className="user-card">
<h3>{name}</h3>
<p>Age: {age}</p>
<p>Email: {email}</p>
</div>
);
}, (prevProps, nextProps) => {
// Custom comparison function
return (
prevProps.name === nextProps.name &&
prevProps.age === nextProps.age
);
});
These answers provide comprehensive coverage of React concepts at a technical lead level, with practical coding examples that demonstrate real-world usage patterns.
76. What is PureComponent?
PureComponent is a base class that implements shouldComponentUpdate() with a shallow comparison of props and state. It automatically prevents unnecessary re-renders when props or state haven't changed.
import React, { PureComponent } from 'react';
class UserProfile extends PureComponent {
render() {
const { user } = this.props;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
}
// This component will only re-render if user object reference changes
// Not if individual properties change (unless you create a new object)
77. What is the difference between PureComponent and Component?
| Aspect | Component | PureComponent |
|---|---|---|
| Re-rendering | Always re-renders when parent re-renders | Only re-renders if props/state change |
| Performance | No built-in optimization | Automatic shallow comparison |
| Use Case | When you need full control | When you want automatic optimization |
| Comparison | No automatic comparison | Shallow comparison of props and state |
// Regular Component - re-renders every time
class RegularComponent extends React.Component {
render() {
console.log('RegularComponent rendered');
return <div>{this.props.value}</div>;
}
}
// PureComponent - only re-renders when props change
class OptimizedComponent extends React.PureComponent {
render() {
console.log('PureComponent rendered');
return <div>{this.props.value}</div>;
}
}
// Usage example
function Parent() {
const [count, setCount] = useState(0);
const user = { name: 'John', age: 30 };
return (
<div>
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
{/* This will re-render every time count changes */}
<RegularComponent value="static" />
{/* This will NOT re-render when count changes */}
<OptimizedComponent value="static" />
</div>
);
}
78. What is the use of getDerivedStateFromProps?
getDerivedStateFromProps is a static lifecycle method that allows a component to update its state based on changes in props. It's called before every render.
class UserProfile extends React.Component {
state = {
displayName: '',
isOnline: false
};
static getDerivedStateFromProps(props, state) {
// Update displayName if user prop changes
if (props.user && props.user.name !== state.displayName) {
return {
displayName: props.user.name,
isOnline: props.user.status === 'online'
};
}
// Return null if no state update is needed
return null;
}
render() {
return (
<div>
<h2>{this.state.displayName}</h2>
<span className={this.state.isOnline ? 'online' : 'offline'}>
{this.state.isOnline ? 'Online' : 'Offline'}
</span>
</div>
);
}
}
Modern Alternative with Hooks:
function UserProfile({ user }) {
const [displayName, setDisplayName] = useState('');
const [isOnline, setIsOnline] = useState(false);
useEffect(() => {
if (user) {
setDisplayName(user.name);
setIsOnline(user.status === 'online');
}
}, [user]);
return (
<div>
<h2>{displayName}</h2>
<span className={isOnline ? 'online' : 'offline'}>
{isOnline ? 'Online' : 'Offline'}
</span>
</div>
);
}
79. What is the use of getSnapshotBeforeUpdate?
getSnapshotBeforeUpdate captures information from the DOM before it's potentially changed. It's called right before the DOM is updated and the return value is passed to componentDidUpdate.
class ChatWindow extends React.Component {
state = {
messages: []
};
chatRef = React.createRef();
getSnapshotBeforeUpdate(prevProps, prevState) {
// Check if we're at the bottom of the chat
if (this.chatRef.current) {
const { scrollTop, scrollHeight, clientHeight } = this.chatRef.current;
return {
wasAtBottom: scrollTop + clientHeight >= scrollHeight - 10
};
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// Auto-scroll to bottom if user was already at bottom
if (snapshot && snapshot.wasAtBottom && this.chatRef.current) {
this.chatRef.current.scrollTop = this.chatRef.current.scrollHeight;
}
}
render() {
return (
<div ref={this.chatRef} className="chat-window">
{this.state.messages.map(message => (
<div key={message.id} className="message">
{message.text}
</div>
))}
</div>
);
}
}
80. What is the use of forwardRef?
forwardRef is the established way to receive a ref in a function component on React 18 and earlier. React 19 supports ref as a prop, so new React 19 function components do not require this wrapper. Existing forwardRef components remain supported; match the example to your React version.
export function SearchInput({ ref, ...props }) {
return <input ref={ref} {...props} />;
} // React 19
81. What is the use of React.lazy?
React.lazy enables code splitting by dynamically importing components, reducing the initial bundle size.
import React, { Suspense, lazy } from 'react';
// Lazy load components
const Dashboard = lazy(() => import('./Dashboard'));
const UserProfile = lazy(() => import('./UserProfile'));
const Settings = lazy(() => import('./Settings'));
function App() {
const [currentPage, setCurrentPage] = useState('dashboard');
const renderPage = () => {
switch (currentPage) {
case 'dashboard':
return <Dashboard />;
case 'profile':
return <UserProfile />;
case 'settings':
return <Settings />;
default:
return <Dashboard />;
}
};
return (
<div>
<nav>
<button onClick={() => setCurrentPage('dashboard')}>Dashboard</button>
<button onClick={() => setCurrentPage('profile')}>Profile</button>
<button onClick={() => setCurrentPage('settings')}>Settings</button>
</nav>
<Suspense fallback={<div>Loading...</div>}>
{renderPage()}
</Suspense>
</div>
);
}
// With error boundary
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage with error boundary
function AppWithErrorBoundary() {
return (
<ErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<Dashboard />
</Suspense>
</ErrorBoundary>
);
}
82. How do you test React components?
React components can be tested using various testing libraries and approaches:
// Component to test
function UserCard({ user, onEdit, onDelete }) {
return (
<div className="user-card" data-testid="user-card">
<h3>{user.name}</h3>
<p>{user.email}</p>
<div className="actions">
<button onClick={() => onEdit(user.id)} data-testid="edit-btn">
Edit
</button>
<button onClick={() => onDelete(user.id)} data-testid="delete-btn">
Delete
</button>
</div>
</div>
);
}
// Test file
import { render, screen, fireEvent } from '@testing-library/react';
import UserCard from './UserCard';
describe('UserCard', () => {
const mockUser = {
id: 1,
name: 'John Doe',
email: 'john@example.com'
};
const mockOnEdit = jest.fn();
const mockOnDelete = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
test('renders user information correctly', () => {
render(
<UserCard
user={mockUser}
onEdit={mockOnEdit}
onDelete={mockOnDelete}
/>
);
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('john@example.com')).toBeInTheDocument();
});
test('calls onEdit when edit button is clicked', () => {
render(
<UserCard
user={mockUser}
onEdit={mockOnEdit}
onDelete={mockOnDelete}
/>
);
fireEvent.click(screen.getByTestId('edit-btn'));
expect(mockOnEdit).toHaveBeenCalledWith(1);
});
test('calls onDelete when delete button is clicked', () => {
render(
<UserCard
user={mockUser}
onEdit={mockOnEdit}
onDelete={mockOnDelete}
/>
);
fireEvent.click(screen.getByTestId('delete-btn'));
expect(mockOnDelete).toHaveBeenCalledWith(1);
});
});
83. What is Jest?
Jest is a JavaScript testing framework developed by Facebook that provides a complete testing solution with built-in assertion library, mocking capabilities, and test runner.
// jest.config.js
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
moduleNameMapping: {
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
'\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/__mocks__/fileMock.js'
},
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
'!src/index.js'
]
};
// Example test with Jest
import { sum, multiply, asyncOperation } from './math';
describe('Math functions', () => {
describe('sum', () => {
test('adds two positive numbers correctly', () => {
expect(sum(2, 3)).toBe(5);
});
test('handles negative numbers', () => {
expect(sum(-1, -2)).toBe(-3);
});
test('returns 0 for empty arguments', () => {
expect(sum()).toBe(0);
});
});
describe('multiply', () => {
test('multiplies two numbers correctly', () => {
expect(multiply(4, 5)).toBe(20);
});
});
describe('asyncOperation', () => {
test('resolves with correct value', async () => {
const result = await asyncOperation(10);
expect(result).toBe(20);
});
test('rejects with error for invalid input', async () => {
await expect(asyncOperation(-1)).rejects.toThrow('Invalid input');
});
});
});
84. What is Enzyme?
Enzyme is a legacy testing utility centered on component inspection and shallow rendering. Its adapter support is version-specific; do not assume older adapters work with a modern React release. For current examples, prefer user-observable tests with a supported React Testing Library and test-runner configuration.
85. What is React Testing Library?
React Testing Library is a testing utility that focuses on testing components the way users interact with them, promoting better testing practices.
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import LoginForm from './LoginForm';
describe('LoginForm', () => {
test('submits form with correct data', async () => {
const mockOnSubmit = jest.fn();
const user = userEvent.setup();
render(<LoginForm onSubmit={mockOnSubmit} />);
// Fill in form fields
await user.type(screen.getByLabelText(/email/i), 'test@example.com');
await user.type(screen.getByLabelText(/password/i), 'password123');
// Submit form
await user.click(screen.getByRole('button', { name: /sign in/i }));
expect(mockOnSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123'
});
});
test('shows validation errors for invalid input', async () => {
const user = userEvent.setup();
render(<LoginForm onSubmit={jest.fn()} />);
// Try to submit empty form
await user.click(screen.getByRole('button', { name: /sign in/i }));
expect(screen.getByText(/email is required/i)).toBeInTheDocument();
expect(screen.getByText(/password is required/i)).toBeInTheDocument();
});
test('shows loading state during submission', async () => {
const mockOnSubmit = jest.fn(() => new Promise(resolve => setTimeout(resolve, 100)));
const user = userEvent.setup();
render(<LoginForm onSubmit={mockOnSubmit} />);
await user.type(screen.getByLabelText(/email/i), 'test@example.com');
await user.type(screen.getByLabelText(/password/i), 'password123');
await user.click(screen.getByRole('button', { name: /sign in/i }));
expect(screen.getByText(/signing in/i)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /signing in/i })).toBeDisabled();
});
});
86. How do you mock API calls in React tests?
// Component with API call
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
try {
const response = await fetch('/api/users');
const data = await response.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// Test with API mocking
import { render, screen, waitFor } from '@testing-library/react';
// Mock fetch globally
global.fetch = jest.fn();
describe('UserList', () => {
beforeEach(() => {
fetch.mockClear();
});
test('renders users after successful API call', async () => {
const mockUsers = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
];
fetch.mockResolvedValueOnce({
ok: true,
json: async () => mockUsers
});
render(<UserList />);
// Initially shows loading
expect(screen.getByText('Loading...')).toBeInTheDocument();
// Wait for users to load
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
expect(screen.getByText('Jane Smith')).toBeInTheDocument();
expect(fetch).toHaveBeenCalledWith('/api/users');
});
test('shows error message on API failure', async () => {
fetch.mockRejectedValueOnce(new Error('Network error'));
render(<UserList />);
await waitFor(() => {
expect(screen.getByText('Error: Network error')).toBeInTheDocument();
});
});
});
// Using MSW (Mock Service Worker) for more realistic API mocking
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('/api/users', (req, res, ctx) => {
return res(
ctx.json([
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
])
);
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
87. What is the use of act() in React Testing Library?
act() ensures that all updates related to React state changes have been processed and applied to the DOM before making assertions.
import { render, screen, act } from '@testing-library/react';
import { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
setCount(prev => prev + 1);
}, 100);
return () => clearTimeout(timer);
}, []);
return <div>Count: {count}</div>;
}
describe('Counter', () => {
test('updates count after timeout', async () => {
jest.useFakeTimers();
render(<Counter />);
expect(screen.getByText('Count: 0')).toBeInTheDocument();
// Fast-forward time
act(() => {
jest.advanceTimersByTime(100);
});
expect(screen.getByText('Count: 1')).toBeInTheDocument();
jest.useRealTimers();
});
test('handles multiple state updates', async () => {
const { rerender } = render(<Counter />);
// Simulate multiple rapid state changes
act(() => {
// This ensures all state updates are processed
rerender(<Counter />);
});
// Now we can safely make assertions
expect(screen.getByText(/Count:/)).toBeInTheDocument();
});
});
88. What is the difference between shallow and mount in Enzyme?
| Aspect | shallow() | mount() |
|---|---|---|
| Rendering | Renders only the component, not children | Renders component and all children |
| Performance | Faster, less memory usage | Slower, more memory usage |
| Use Case | Unit testing, isolated component testing | Integration testing, full component tree |
| Child Components | Not rendered (stubbed) | Fully rendered |
| Lifecycle Methods | Only component's lifecycle | All children's lifecycle methods |
import { shallow, mount } from 'enzyme';
// Child component
function ChildComponent({ data }) {
return <div className="child">{data}</div>;
}
// Parent component
function ParentComponent({ items }) {
return (
<div className="parent">
<h1>Parent</h1>
{items.map(item => (
<ChildComponent key={item.id} data={item.name} />
))}
</div>
);
}
describe('ParentComponent', () => {
const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
];
test('shallow rendering - only parent component', () => {
const wrapper = shallow(<ParentComponent items={items} />);
// Child components are not rendered
expect(wrapper.find(ChildComponent)).toHaveLength(2);
expect(wrapper.find('.child')).toHaveLength(0); // No child divs
// Only parent structure is rendered
expect(wrapper.find('h1').text()).toBe('Parent');
});
test('mount rendering - full component tree', () => {
const wrapper = mount(<ParentComponent items={items} />);
// Child components are fully rendered
expect(wrapper.find(ChildComponent)).toHaveLength(2);
expect(wrapper.find('.child')).toHaveLength(2); // Child divs are rendered
// Can access child component content
expect(wrapper.find('.child').first().text()).toBe('Item 1');
expect(wrapper.find('.child').last().text()).toBe('Item 2');
});
});
89. What is the use of snapshot testing?
Snapshot testing captures a serialized version of your component's output and compares it against future changes to detect unexpected modifications.
import renderer from 'react-test-renderer';
import UserCard from './UserCard';
describe('UserCard Snapshot Tests', () => {
test('matches snapshot for user card', () => {
const user = {
id: 1,
name: 'John Doe',
email: 'john@example.com'
};
const tree = renderer
.create(<UserCard user={user} onEdit={jest.fn()} onDelete={jest.fn()} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
test('matches snapshot for different user data', () => {
const user = {
id: 2,
name: 'Jane Smith',
email: 'jane@example.com'
};
const tree = renderer
.create(<UserCard user={user} onEdit={jest.fn()} onDelete={jest.fn()} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
// Snapshot file example (__snapshots__/UserCard.test.js.snap)
/*
exports[`UserCard Snapshot Tests matches snapshot for user card 1`] = `
<div
className="user-card"
data-testid="user-card"
>
<h3>
John Doe
</h3>
<p>
john@example.com
</p>
<div
className="actions"
>
<button
data-testid="edit-btn"
onClick={[Function]}
>
Edit
</button>
<button
data-testid="delete-btn"
onClick={[Function]}
>
Delete
</button>
</div>
</div>
`;
*/
90. How do you deploy a React application?
Here are the main deployment strategies for React applications:
1. Static Hosting (Netlify, Vercel, GitHub Pages)
Build the application
npm run build
Deploy to Netlify
netlify deploy --prod --dir=build
Deploy to Vercel
vercel --prod
Deploy to GitHub Pages
npm run deploy
2. Docker Deployment
Dockerfile
FROM node:16-alpine as build
WORKDIR /app COPY package*.json ./ RUN npm ci --only=production
COPY . . RUN npm run build
FROM nginx:alpine COPY --from=build /app/build /usr/share/nginx/html COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
# nginx.conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
3. AWS S3 + CloudFront
// deploy.js
const AWS = require('aws-sdk');
const fs = require('fs');
const path = require('path');
const s3 = new AWS.S3();
const cloudfront = new AWS.CloudFront();
async function deployToS3() {
const buildPath = path.join(__dirname, 'build');
// Upload all files from build directory
const uploadFile = async (filePath) => {
const fileContent = fs.readFileSync(filePath);
const key = path.relative(buildPath, filePath);
const params = {
Bucket: 'your-bucket-name',
Key: key,
Body: fileContent,
ContentType: getContentType(key)
};
await s3.upload(params).promise();
};
// Recursively upload all files
const uploadDirectory = async (dir) => {
const files = fs.readdirSync(dir);
for (const file of files) {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
await uploadDirectory(filePath);
} else {
await uploadFile(filePath);
}
}
};
await uploadDirectory(buildPath);
console.log('Deployment completed!');
}
function getContentType(filename) {
const ext = path.extname(filename);
const types = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.svg': 'image/svg+xml'
};
return types[ext] || 'application/octet-stream';
}
deployToS3().catch(console.error);
4. CI/CD Pipeline (GitHub Actions)
# .github/workflows/deploy.yml
name: Deploy React App
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test -- --coverage --watchAll=false
- name: Build application
run: npm run build
env:
REACT_APP_API_URL: ${{ secrets.REACT_APP_API_URL }}
- name: Deploy to production
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./build
5. Environment Configuration
// .env.production
REACT_APP_API_URL=https://api.production.com
REACT_APP_GA_TRACKING_ID=GA-XXXXXXXXX
REACT_APP_SENTRY_DSN=https://xxxxx@sentry.io/xxxxx
// src/config/environment.js
const config = {
apiUrl: process.env.REACT_APP_API_URL,
gaTrackingId: process.env.REACT_APP_GA_TRACKING_ID,
sentryDsn: process.env.REACT_APP_SENTRY_DSN,
isProduction: process.env.NODE_ENV === 'production'
};
export default config;
These deployment strategies provide different levels of complexity and features, from simple static hosting to enterprise-grade deployments with CDN, monitoring, and CI/CD pipelines.
91. What is create-react-app?
Create React App was a preconfigured React build toolchain. It was deprecated for new apps in February 2025. Existing applications can continue while planning a migration to a suitable framework or maintained build tool. Reference: Create React App deprecation.
92. How do you eject from create-react-app?
In an existing Create React App project, npm run eject exposes its build configuration and scripts. The tool offers no automatic uneject operation; source-control recovery is the usual way back. Ejecting is not a modernization strategy by itself. Consider a planned migration because CRA is deprecated for new applications. Reference: CRA deprecation.
93. What is the use of environment variables in React?
Environment variable conventions belong to the build tool, not React itself: legacy CRA uses REACT_APP_ and Vite uses VITE_ with import.meta.env. Browser-exposed variables are generally embedded in the bundle and are not secrets. Runtime configuration requires a separately designed delivery mechanism. Keep private API keys and credentials on the server.
94. How do you manage side effects in React?
useEffect synchronizes a committed component with an external system. List every reactive dependency; cleanup runs before the effect is re-established after dependency changes and on unmount. Strict Mode adds a development-only setup/cleanup check. An empty dependency array does not promise a single execution over the application lifetime. Effects do not run during server rendering. Reference: useEffect.
95. What is the use of useReducer?
Answer: useReducer is a React hook that manages complex state logic in a predictable way, similar to Redux. It's useful when you have complex state that involves multiple sub-values or when the next state depends on the previous one.
Basic Example:
import React, { useReducer } from 'react';
const initialState = {
count: 0,
isLoading: false,
error: null
};
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
case 'SET_LOADING':
return { ...state, isLoading: action.payload };
case 'SET_ERROR':
return { ...state, error: action.payload };
case 'RESET':
return initialState;
default:
throw new Error(`Unknown action type: ${action.type}`);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>
Increment
</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>
Decrement
</button>
<button onClick={() => dispatch({ type: 'RESET' })}>
Reset
</button>
</div>
);
}
Complex State Management:
// Shopping cart example
const cartInitialState = {
items: [],
total: 0,
discount: 0,
shipping: 0
};
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
const existingItem = state.items.find(item => item.id === action.payload.id);
if (existingItem) {
return {
...state,
items: state.items.map(item =>
item.id === action.payload.id
? { ...item, quantity: item.quantity + 1 }
: item
)
};
}
return {
...state,
items: [...state.items, { ...action.payload, quantity: 1 }]
};
case 'REMOVE_ITEM':
return {
...state,
items: state.items.filter(item => item.id !== action.payload)
};
case 'UPDATE_QUANTITY':
return {
...state,
items: state.items.map(item =>
item.id === action.payload.id
? { ...item, quantity: action.payload.quantity }
: item
)
};
case 'CALCULATE_TOTAL':
const subtotal = state.items.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);
const total = subtotal - state.discount + state.shipping;
return { ...state, total };
default:
return state;
}
}
96. What is the difference between useReducer and useState?
Answer: Both manage state, but they serve different purposes and have different use cases.
useState: - Simple state management - Direct state updates - Good for primitive values or simple objects - Less boilerplate
useReducer: - Complex state logic - Predictable state transitions - Better for objects with multiple properties - Easier testing and debugging
Comparison Example:
// Using useState
function TodoList() {
const [todos, setTodos] = useState([]);
const [filter, setFilter] = useState('all');
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text, completed: false }]);
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div>
{/* Component JSX */}
</div>
);
}
// Using useReducer
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
)
};
case 'DELETE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
};
case 'SET_FILTER':
return { ...state, filter: action.payload };
default:
return state;
}
};
function TodoListWithReducer() {
const [state, dispatch] = useReducer(todoReducer, {
todos: [],
filter: 'all'
});
const addTodo = (text) => dispatch({ type: 'ADD_TODO', payload: text });
const toggleTodo = (id) => dispatch({ type: 'TOGGLE_TODO', payload: id });
const deleteTodo = (id) => dispatch({ type: 'DELETE_TODO', payload: id });
return (
<div>
{/* Component JSX */}
</div>
);
}
97. What is the use of useImperativeHandle?
Answer: useImperativeHandle customizes the instance value that is exposed to parent components when using ref. It allows you to control what methods or properties are accessible from the parent component.
Example:
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
// Child component
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
blur: () => {
inputRef.current.blur();
},
getValue: () => {
return inputRef.current.value;
},
setValue: (value) => {
inputRef.current.value = value;
},
select: () => {
inputRef.current.select();
}
}));
return (
<input
ref={inputRef}
type="text"
placeholder="Enter text..."
{...props}
/>
);
});
// Parent component
function ParentComponent() {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.focus();
};
const handleGetValue = () => {
const value = inputRef.current.getValue();
console.log('Input value:', value);
};
const handleSetValue = () => {
inputRef.current.setValue('New value');
};
return (
<div>
<CustomInput ref={inputRef} />
<button onClick={handleFocus}>Focus Input</button>
<button onClick={handleGetValue}>Get Value</button>
<button onClick={handleSetValue}>Set Value</button>
</div>
);
}
Advanced Example - Form Validation:
const ValidatedInput = forwardRef(({ validationRules, ...props }, ref) => {
const inputRef = useRef(null);
const [errors, setErrors] = useState([]);
const validate = (value) => {
const newErrors = [];
if (validationRules.required && !value) {
newErrors.push('This field is required');
}
if (validationRules.minLength && value.length < validationRules.minLength) {
newErrors.push(`Minimum length is ${validationRules.minLength}`);
}
if (validationRules.pattern && !validationRules.pattern.test(value)) {
newErrors.push('Invalid format');
}
setErrors(newErrors);
return newErrors.length === 0;
};
useImperativeHandle(ref, () => ({
validate: () => validate(inputRef.current.value),
getValue: () => inputRef.current.value,
setValue: (value) => {
inputRef.current.value = value;
validate(value);
},
focus: () => inputRef.current.focus(),
getErrors: () => errors
}));
return (
<div>
<input
ref={inputRef}
onChange={(e) => validate(e.target.value)}
{...props}
/>
{errors.map((error, index) => (
<div key={index} style={{ color: 'red', fontSize: '12px' }}>
{error}
</div>
))}
</div>
);
});
98. What is the use of useDebugValue?
Answer: useDebugValue is used to display a label for custom hooks in React DevTools. It helps with debugging by showing meaningful information about the hook's state.
Basic Example:
import { useState, useDebugValue } from 'react';
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
useDebugValue(`Count: ${count}`);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
}
function Counter() {
const { count, increment, decrement, reset } = useCounter(5);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}
Advanced Example with Conditional Debug Value:
function useApiCall(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useDebugValue(
loading ? 'Loading...' :
error ? `Error: ${error.message}` :
`Data: ${data ? 'Loaded' : 'Not loaded'}`
);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
Performance Optimization:
function useExpensiveCalculation(value) {
const [result, setResult] = useState(null);
useDebugValue(
result,
(value) => `Expensive calculation result: ${value}`
);
useEffect(() => {
// Expensive calculation
const calculated = performExpensiveCalculation(value);
setResult(calculated);
}, [value]);
return result;
}
99. How do you handle authentication in React?
Authorize every operation on the server; a hidden button or protected client route is only UI behavior. Client bundles and client environment variables are public. Prefer established authentication flows and carefully chosen session storage. HttpOnly, Secure cookies limit JavaScript token access but require CSRF defenses and appropriate SameSite settings. Tokens in localStorage are exposed to script running in the origin. Escape text, sanitize any intentionally rendered HTML, and apply an appropriate content-security policy. Hashing or encrypting a token with a secret shipped in the same bundle does not make it secret.
100. How do you secure a React application?
Authorize every operation on the server; a hidden button or protected client route is only UI behavior. Client bundles and client environment variables are public. Prefer established authentication flows and carefully chosen session storage. HttpOnly, Secure cookies limit JavaScript token access but require CSRF defenses and appropriate SameSite settings. Tokens in localStorage are exposed to script running in the origin. Escape text, sanitize any intentionally rendered HTML, and apply an appropriate content-security policy. Hashing or encrypting a token with a secret shipped in the same bundle does not make it secret.