Complete React Interview Preparation Guide
1. How React Works Under the Hood (Explained Simply)
Think of React like a smart helper that builds websites using blocks (components). Here's how it works:
Virtual DOM: React creates a fake copy of your webpage in memory (like a blueprint)
Diffing: When something changes, React compares the old blueprint with the new one
Reconciliation: React only updates the parts that actually changed, making it super fast
Components: Everything is built with reusable pieces, like LEGO blocks
2. What Problem Did React Come to Solve?
Before React, developers faced these challenges:
DOM Manipulation: Manually updating HTML was slow and error-prone
State Management: Keeping track of data changes was complex
Reusability: Code was hard to reuse across different parts of an app
Performance: Updating entire pages was inefficient
React solved these by introducing:
Virtual DOM for efficient updates
Component-based architecture
Declarative programming (describe what you want, not how to do it)
Unidirectional data flow
3. Composable Components
Components that can be combined to build larger, more complex UIs:
javascript
// Small, focused components
const Button = ({ onClick, children }) => (
<button onClick={onClick}>{children}</button>
);
const Input = ({ value, onChange }) => (
<input value={value} onChange={onChange} />
);
// Composed into larger components
const LoginForm = () => {
const [username, setUsername] = useState('');
return (
<form>
<Input value={username} onChange={(e) => setUsername(e.target.value)} />
<Button onClick={handleLogin}>Login</Button>
</form>
);
};
4. Separating Logic and Style
CSS Modules
javascript
// styles.module.css
.button { background: blue; }
// Component.js
import styles from './styles.module.css';
const Button = () => <button className={styles.button}>Click me</button>;
Styled Components
javascript
import styled from 'styled-components';
const StyledButton = styled.button`
background: blue;
color: white;
`;
Custom Hooks for Logic
javascript
// useAuth.js (logic)
const useAuth = () => {
const [user, setUser] = useState(null);
const login = (credentials) => { /* logic */ };
return { user, login };
};
// Component.js (presentation)
const LoginButton = () => {
const { user, login } = useAuth();
return <button onClick={login}>Login</button>;
};
5. Security Measures in TypeScript Frontend
Input Validation
typescript
interface UserInput {
email: string;
password: string;
}
const validateInput = (input: UserInput): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(input.email) && input.password.length >= 8;
};
Sanitization
typescript
import DOMPurify from 'dompurify';
const sanitizeHTML = (html: string): string => {
return DOMPurify.sanitize(html);
};
Secure API Calls
typescript
const apiCall = async (endpoint: string, data: any) => {
const token = localStorage.getItem('token');
return fetch(endpoint, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
};
6. Form Input Validation
Using Formik + Yup
javascript
import { Formik, Form, Field } from 'formik';
import * as Yup from 'yup';
const validationSchema = Yup.object({
email: Yup.string().email('Invalid email').required('Required'),
password: Yup.string().min(8, 'Too short').required('Required'),
});
const LoginForm = () => (
<Formik
initialValues={{ email: '', password: '' }}
validationSchema={validationSchema}
onSubmit={(values) => console.log(values)}
>
<Form>
<Field name="email" type="email" />
<Field name="password" type="password" />
<button type="submit">Submit</button>
</Form>
</Formik>
);
Custom Validation Hook
javascript
const useValidation = (value, rules) => {
const [error, setError] = useState('');
useEffect(() => {
const validateField = () => {
for (const rule of rules) {
if (!rule.test(value)) {
setError(rule.message);
return;
}
}
setError('');
};
validateField();
}, [value, rules]);
return error;
};
7. Typesafe Components Design
Generic Components
typescript
interface Props<T> {
data: T[];
renderItem: (item: T) => React.ReactNode;
keyExtractor: (item: T) => string;
}
const List = <T,>({ data, renderItem, keyExtractor }: Props<T>) => (
<ul>
{data.map(item => (
<li key={keyExtractor(item)}>{renderItem(item)}</li>
))}
</ul>
);
Union Types for Props
typescript
interface ButtonProps {
variant: 'primary' | 'secondary' | 'danger';
size: 'small' | 'medium' | 'large';
onClick: () => void;
children: React.ReactNode;
}
const Button: React.FC<ButtonProps> = ({ variant, size, onClick, children }) => (
<button className={`btn btn-${variant} btn-${size}`} onClick={onClick}>
{children}
</button>
);
8. Accessible Components
ARIA Attributes
javascript
const Modal = ({ isOpen, onClose, children }) => {
if (!isOpen) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<div className="modal-content">
<h2 id="modal-title">Modal Title</h2>
{children}
<button onClick={onClose} aria-label="Close modal">×</button>
</div>
</div>
);
};
Keyboard Navigation
javascript
const DropdownMenu = () => {
const [isOpen, setIsOpen] = useState(false);
const handleKeyDown = (e) => {
if (e.key === 'Escape') setIsOpen(false);
if (e.key === 'Enter' || e.key === ' ') setIsOpen(!isOpen);
};
return (
<div>
<button
onClick={() => setIsOpen(!isOpen)}
onKeyDown={handleKeyDown}
aria-expanded={isOpen}
aria-haspopup="true"
>
Menu
</button>
{isOpen && <ul role="menu">...</ul>}
</div>
);
};
9. State Management: Redux vs Context API
When to Use Context API
Simple, localized state
Small to medium applications
Minimal boilerplate needed
javascript
const ThemeContext = createContext();
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
When to Use Redux
Complex state logic
Large applications
Time-travel debugging needed
Predictable state updates
javascript
// Redux store
const initialState = { count: 0 };
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
default:
return state;
}
};
10. State Management Options
Redux
Pros: Predictable, time-travel debugging, great DevTools
Cons: Boilerplate, learning curve
Use: Large apps, complex state logic
Context API
Pros: Built-in, simple, no extra dependencies
Cons: Performance issues with frequent updates
Use: Small to medium apps, simple state
MobX
Pros: Simple, less boilerplate, reactive
Cons: Less predictable, harder to debug
Use: When you prefer reactive programming
11. Hooks Deep Dive
useState
javascript
const [count, setCount] = useState(0);
// Functional updates
setCount(prevCount => prevCount + 1);
// Lazy initialization
const [state, setState] = useState(() => {
return computeExpensiveValue();
});
useEffect
javascript
useEffect(() => {
// Side effect
const subscription = subscribeToSomething();
// Cleanup
return () => subscription.unsubscribe();
}, [dependency]); // Dependency array
Custom Hooks
javascript
const useLocalStorage = (key, initialValue) => {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = (value) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
};
12. Performance Optimization
React.memo
javascript
const ExpensiveComponent = React.memo(({ data }) => {
return <div>{/* Expensive rendering */}</div>;
});
// With custom comparison
const MyComponent = React.memo(({ user }) => {
return <div>{user.name}</div>;
}, (prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
});
useMemo
javascript
const ExpensiveComponent = ({ items }) => {
const expensiveValue = useMemo(() => {
return items.reduce((sum, item) => sum + item.value, 0);
}, [items]);
return <div>{expensiveValue}</div>;
};
useCallback
javascript
const Parent = ({ items }) => {
const handleClick = useCallback((id) => {
// Handle click
}, []);
return (
<div>
{items.map(item => (
<Child key={item.id} item={item} onClick={handleClick} />
))}
</div>
);
};
13. Server-Side Rendering and Next.js
Benefits of SSR
Better SEO
Faster initial page load
Better performance on slow devices
Next.js Features
javascript
// pages/index.js
export default function Home({ data }) {
return <div>{data.title}</div>;
}
// Static Site Generation
export async function getStaticProps() {
const data = await fetchData();
return { props: { data } };
}
// Server-Side Rendering
export async function getServerSideProps() {
const data = await fetchData();
return { props: { data } };
}
14. Testing React Components
Jest + React Testing Library
javascript
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
test('renders button with text', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
test('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
Testing Hooks
javascript
import { renderHook, act } from '@testing-library/react';
import useCounter from './useCounter';
test('should increment counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
15. Component Lifecycle
Class Components
javascript
class MyComponent extends React.Component {
componentDidMount() {
// After component mounts
}
componentDidUpdate(prevProps, prevState) {
// After component updates
}
componentWillUnmount() {
// Before component unmounts
}
render() {
return <div>Hello</div>;
}
}
Functional Components (Hooks)
javascript
const MyComponent = () => {
useEffect(() => {
// componentDidMount
return () => {
// componentWillUnmount
};
}, []);
useEffect(() => {
// componentDidUpdate
});
return <div>Hello</div>;
};
16. React Router
Basic Setup
javascript
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
const App = () => (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users/:id" element={<User />} />
</Routes>
</BrowserRouter>
);
Navigation Hooks
javascript
import { useNavigate, useParams } from 'react-router-dom';
const User = () => {
const { id } = useParams();
const navigate = useNavigate();
const handleBack = () => navigate(-1);
return <div>User {id}</div>;
};
17. Error Boundaries
Class-based Error Boundary
javascript
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
Usage
javascript
const App = () => (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
18. PropTypes and TypeScript
PropTypes
javascript
import PropTypes from 'prop-types';
const Button = ({ text, onClick, variant }) => (
<button onClick={onClick} className={variant}>
{text}
</button>
);
Button.propTypes = {
text: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
variant: PropTypes.oneOf(['primary', 'secondary'])
};
TypeScript Integration
typescript
interface ButtonProps {
text: string;
onClick: () => void;
variant?: 'primary' | 'secondary';
}
const Button: React.FC<ButtonProps> = ({ text, onClick, variant = 'primary' }) => (
<button onClick={onClick} className={variant}>
{text}
</button>
);
19. React Portals and Refs
Portals
javascript
import { createPortal } from 'react-dom';
const Modal = ({ children }) => {
return createPortal(
<div className="modal">
{children}
</div>,
document.getElementById('modal-root')
);
};
Refs
javascript
const FocusableInput = () => {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current?.focus();
};
return (
<div>
<input ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</div>
);
};
20. React Rendering Lifecycle
What Triggers Re-renders
State changes
Props changes
Parent component re-renders
Context value changes
Render Process
1. Trigger: Something causes a re-render
2. Render: React calls component functions
3. Commit: React applies changes to DOM
React Fiber Benefits
Incremental rendering
Ability to pause and resume work
Priority-based scheduling
Better error handling
21. Controlled vs Uncontrolled Components
Controlled Component
javascript
const ControlledInput = () => {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
};
Uncontrolled Component
javascript
const UncontrolledInput = () => {
const inputRef = useRef();
const handleSubmit = () => {
console.log(inputRef.current.value);
};
return (
<div>
<input ref={inputRef} defaultValue="initial" />
<button onClick={handleSubmit}>Submit</button>
</div>
);
};
22. Reconciliation and React Fiber
Reconciliation Process
1. Compare new element tree with previous
2. Identify differences (diffing)
3. Update only changed parts
4. Maintain component state where possible
React Fiber Improvements
Incremental Rendering: Break work into chunks
Prioritization: High-priority updates first
Pausing: Pause work for browser painting
Error Boundaries: Better error handling
23. Performance Optimization Techniques
Code Splitting
javascript
import { lazy, Suspense } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
const App = () => (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
Virtualization
javascript
import { FixedSizeList as List } from 'react-window';
const VirtualizedList = ({ items }) => (
<List
height={400}
itemCount={items.length}
itemSize={35}
>
{({ index, style }) => (
<div style={style}>
{items[index]}
</div>
)}
</List>
);
24. Higher-Order Components (HOCs)
Creating HOCs
javascript
const withLoading = (WrappedComponent) => {
return function WithLoadingComponent(props) {
if (props.isLoading) {
return <div>Loading...</div>;
}
return <WrappedComponent {...props} />;
};
};
// Usage
const EnhancedComponent = withLoading(MyComponent);
When to Use HOCs
Cross-cutting concerns
Reusable logic
Props manipulation
Conditional rendering
25. useEffect Dependency Array
No Dependency Array
javascript
useEffect(() => {
// Runs after every render
});
Empty Dependency Array
javascript
useEffect(() => {
// Runs only once after mount
}, []);
With Dependencies
javascript
useEffect(() => {
// Runs when dependencies change
}, [dependency1, dependency2]);
26. Context vs Redux
Use Context When
Simple state
Small to medium apps
Minimal boilerplate needed
Component tree is not too deep
Use Redux When
Complex state logic
Large applications
Time-travel debugging needed
Predictable state updates required
27. Preventing Memory Leaks
Cleanup in useEffect
javascript
useEffect(() => {
const subscription = subscribeToSomething();
return () => {
subscription.unsubscribe();
};
}, []);
Canceling Async Operations
javascript
useEffect(() => {
let isCancelled = false;
const fetchData = async () => {
const result = await api.getData();
if (!isCancelled) {
setData(result);
}
};
fetchData();
return () => {
isCancelled = true;
};
}, []);
28. Authentication and Security
JWT Handling
javascript
const useAuth = () => {
const [token, setToken] = useState(localStorage.getItem('token'));
const login = async (credentials) => {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
const { token } = await response.json();
setToken(token);
localStorage.setItem('token', token);
};
const logout = () => {
setToken(null);
localStorage.removeItem('token');
};
return { token, login, logout };
};
CSRF Protection
javascript
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify(data)
});
29. Accessibility in React
ARIA Attributes
javascript
const AccessibleButton = ({ onClick, children, disabled }) => (
<button
onClick={onClick}
disabled={disabled}
aria-disabled={disabled}
aria-label={typeof children === 'string' ? children : 'Button'}
>
{children}
</button>
);
Focus Management
javascript
const Modal = ({ isOpen, onClose }) => {
const modalRef = useRef();
useEffect(() => {
if (isOpen && modalRef.current) {
modalRef.current.focus();
}
}, [isOpen]);
return isOpen ? (
<div
ref={modalRef}
role="dialog"
aria-modal="true"
tabIndex={-1}
>
Modal content
</div>
) : null;
};
Cross-Browser Compatibility
CSS Inconsistencies
css
/* Use CSS Reset */
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* Vendor prefixes */
.rounded {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
Polyfills
javascript
// Check if feature exists
if (!Array.prototype.includes) {
// Add polyfill
Array.prototype.includes = function(searchElement) {
return this.indexOf(searchElement) !== -1;
};
}
Debugging Browser Issues
Use browser DevTools
Check console for errors
Test in different browsers
Use feature detection libraries
Security Best Practices
Preventing XSS
javascript
// Don't use dangerouslySetInnerHTML with user input
const SafeComponent = ({ userInput }) => {
// This is safe - React escapes by default
return <div>{userInput}</div>;
};
// If you must use HTML, sanitize it
import DOMPurify from 'dompurify';
const UnsafeComponent = ({ htmlContent }) => {
const sanitizedHTML = DOMPurify.sanitize(htmlContent);
return <div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />;
};
Handling CORS
javascript
// Frontend configuration
const apiCall = async (url, options) => {
const response = await fetch(url, {
...options,
credentials: 'include', // Include cookies
headers: {
'Content-Type': 'application/json',
...options.headers
}
});
return response.json();
};
Interview Tips
Common Questions to Prepare For
1. Explain the virtual DOM
2. What are hooks and why were they introduced?
3. How do you optimize React performance?
4. Difference between controlled and uncontrolled components
5. How does React handle state updates?
6. What is the purpose of keys in React lists?
7. How do you handle forms in React?
8. Explain the component lifecycle
Code Challenge Preparation
Practice building components from scratch
Understand async operations with useEffect
Know how to handle forms and validation
Practice state management patterns
Understand testing basics
Best Practices to Mention
Keep components small and focused
Use TypeScript for type safety
Implement proper error boundaries
Follow accessibility guidelines
Write tests for your components
Use meaningful component and variable names
Implement proper loading and error states
Remember: Practice coding examples, understand the "why" behind each concept, and be ready to
discuss trade-offs and alternatives!