React Theory
Virtual DOM, Fiber, hooks, performance, concurrent mode and more
14 questions
Must Revise
Hard What is the Virtual DOM and how does reconciliation work?
Virtual DOM Reconciliation Diffing React Core Asked at: Meta Google Amazon Flipkart
What is the Virtual DOM and how does reconciliation work?
Virtual DOM
The Virtual DOM is a lightweight JavaScript representation of the actual DOM tree. React keeps a copy in memory and uses it to compute minimal real DOM updates.
State/Props Change
โ
โผ
โโโโโโโโโโโโโโโโโโโโ
โ Render Phase โ (pure, can be paused/aborted)
โ Creates new โ
โ Virtual DOM โ
โโโโโโโโฌโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโ
โ Diffing โ O(n) algorithm with heuristics:
โ Algorithm โ 1. Different types โ replace subtree
โ โ 2. Same type โ update attributes
โ โ 3. Use keys for lists
โโโโโโโโฌโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโ
โ Commit Phase โ (synchronous, cannot be interrupted)
โ Apply minimal โ
โ DOM mutations โ
โโโโโโโโโโโโโโโโโโโโ
Diffing Heuristics
- โข Different element types โ destroy old tree, build new tree from scratch
- โข Same element type โ update only changed attributes
- โข Children with keys โ O(n) matching instead of O(nยณ) brute force
- โข Keys must be stable โ avoid index as key for dynamic lists
// โ Bad: index as key
{items.map((item, index) => (
<Item key={index} {...item} />
))}
// Problem: inserting at beginning = all keys shift
// React re-renders everything!
// โ
Good: stable unique id
{items.map(item => (
<Item key={item.id} {...item} />
))}
Must Revise
Hard What is React Fiber architecture?
Fiber Concurrent Mode Scheduler React Internal Asked at: Meta Google
What is React Fiber architecture?
React Fiber (React 16+)
Fiber is React's reconciliation engine. It replaces the old recursive reconciler with an incremental, interruptible one using a linked list of 'fiber' nodes.
Why was it needed? Old reconciler was synchronous โ a large tree render couldn't be interrupted, causing dropped frames and poor UX.
| Feature | Old Reconciler (Stack) | Fiber |
|---|---|---|
| Rendering | Synchronous, recursive | Incremental, interruptible |
| Priority | FIFO | Priority-based scheduling |
| Pausing | Not possible | Can pause and resume |
| Concurrent features | No | Yes (useTransition, Suspense) |
// Each fiber node represents a unit of work
// Fiber node structure (simplified):
const fiberNode = {
type: 'div', // element type
key: null,
stateNode: domNode, // actual DOM node
// Tree structure (linked list โ not tree)
child: firstChildFiber,
sibling: nextSiblingFiber,
return: parentFiber,
// Work
pendingProps: {},
memoizedProps: {},
memoizedState: {},
// Effect
effectTag: 'UPDATE',
nextEffect: null,
};
// React processes one fiber at a time,
// yielding to browser between frames using
// requestIdleCallback / MessageChannel Concurrent Features enabled by Fiber
useTransition (mark state updates as non-urgent), useDeferredValue, Suspense for data fetching, automatic batching, Offscreen component.
Must Revise
Medium All React Hooks explained in depth
Hooks useState useEffect useCallback useMemo useRef Asked at: Meta Google Amazon Flipkart Netflix
All React Hooks explained in depth
| Hook | Purpose | Returns |
|---|---|---|
| useState | Local state | [state, setState] |
| useEffect | Side effects after render | cleanup function |
| useLayoutEffect | Side effects before paint (sync) | cleanup function |
| useCallback | Memoize function reference | memoized function |
| useMemo | Memoize expensive computation | memoized value |
| useRef | Mutable ref / DOM access | { current: value } |
| useContext | Read context value | context value |
| useReducer | Complex state management | [state, dispatch] |
| useImperativeHandle | Customize ref exposed to parent | void |
| useId | Generate unique IDs | string id |
| useTransition | Mark state as non-urgent | [isPending, startTransition] |
| useDeferredValue | Defer non-urgent re-renders | deferred value |
// useEffect โ dependency array patterns
useEffect(() => {
// runs after every render
});
useEffect(() => {
// runs only on mount
return () => cleanup(); // runs on unmount
}, []);
useEffect(() => {
// runs when a or b changes
const sub = api.subscribe(a);
return () => sub.unsubscribe(); // cleanup
}, [a, b]);
// useCallback vs useMemo
const handleClick = useCallback(() => {
doSomething(id);
}, [id]); // memoizes the function itself
const sortedList = useMemo(() => {
return list.sort((a, b) => a.name.localeCompare(b.name));
}, [list]); // memoizes the computed value
// useRef โ two uses
const inputRef = useRef(null); // DOM ref
useEffect(() => { inputRef.current?.focus(); }, []);
const countRef = useRef(0); // mutable value, no re-render
const handleClick2 = () => { countRef.current++; }; // doesn't trigger re-render
// useReducer โ for complex state
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT': return { count: state.count + 1 };
case 'SET': return { count: action.payload };
default: return state;
}
};
const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: 'INCREMENT' });
dispatch({ type: 'SET', payload: 10 });
Must Revise
Hard React Performance Optimization techniques
Performance memo useMemo useCallback Lazy Loading Asked at: Meta Google Amazon Netflix Flipkart
React Performance Optimization techniques
- โข React.memo โ skip re-render if props unchanged
- โข useMemo โ memoize expensive computations
- โข useCallback โ stable function references for memo'd children
- โข Code splitting โ React.lazy + Suspense
- โข Virtualization โ react-window for large lists
- โข Key prop โ stable keys for efficient diffing
- โข Avoid inline objects/functions in JSX
- โข useTransition โ non-urgent state updates
- โข Avoid anonymous functions in render (they recreate every time)
- โข Context splitting โ separate contexts to minimize re-renders
// React.memo โ prevents re-render if props same
const ExpensiveChild = React.memo(({ data, onUpdate }) => {
return <div>{/* expensive render */}</div>;
}, (prevProps, nextProps) => {
// Custom comparison: return true to skip re-render
return prevProps.data.id === nextProps.data.id;
});
// โ Problem: new function on every parent render
function Parent() {
const [count, setCount] = useState(0);
// This creates a new function every render!
const handleUpdate = () => updateData();
return <ExpensiveChild onUpdate={handleUpdate} />;
// ExpensiveChild re-renders every time even though memo!
}
// โ
Fix with useCallback
function Parent() {
const [count, setCount] = useState(0);
const handleUpdate = useCallback(() => updateData(), []);
return <ExpensiveChild onUpdate={handleUpdate} />;
}
// Code splitting
const HeavyPage = React.lazy(() => import('./HeavyPage'));
function App() {
return (
<Suspense fallback={<Spinner />}>
<HeavyPage />
</Suspense>
);
}
// useTransition for filtering large lists
function SearchResults() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
function handleChange(e) {
setQuery(e.target.value); // urgent: update input immediately
startTransition(() => {
setResults(filterResults(e.target.value)); // non-urgent
});
}
return (
<>
<input onChange={handleChange} value={query} />
{isPending ? <Spinner /> : <ResultList results={results} />}
</>
);
}
Must Revise
Medium React Context โ proper usage and pitfalls
Context State Management Performance Asked at: Meta Google Flipkart
React Context โ proper usage and pitfalls
Context Performance Issue
Every consumer re-renders when context value changes. Use context for infrequently updated values (theme, locale, user). For frequent updates, use Zustand/Redux or split contexts.
// Creating and using context
const ThemeContext = createContext('light');
const UserContext = createContext(null);
// Provider โ wrap once at appropriate level
function App() {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState(null);
// โ ๏ธ Problem: new object on every render!
// return <UserContext.Provider value={{ user, setUser }}>
// โ
Memoize context value
const userValue = useMemo(() => ({ user, setUser }), [user]);
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={userValue}>
<Router />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
// Consumer โ custom hook pattern
function useTheme() {
const ctx = useContext(ThemeContext);
if (ctx === undefined) {
throw new Error('useTheme must be used within ThemeProvider');
}
return ctx;
}
// Context splitting to prevent unnecessary re-renders
// โ
Split read and write contexts
const CountStateContext = createContext(0);
const CountDispatchContext = createContext(null);
function Counter() {
const count = useContext(CountStateContext); // only re-renders on count change
const dispatch = useContext(CountDispatchContext); // never re-renders (dispatch is stable)
return <button onClick={() => dispatch('INC')}>{count}</button>;
} Medium Error Boundaries in React
Error Boundaries Error Handling Class Components Asked at: Meta Google Amazon
Error Boundaries in React
Error Boundaries
Error boundaries are React class components that catch JavaScript errors in child component tree, log them, and display fallback UI. They do NOT catch: event handlers, async code, SSR, errors in the boundary itself.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
// Update state to show fallback UI
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// Log to error reporting service
logErrorToService(error, errorInfo.componentStack);
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
<div>
<h2>Something went wrong.</h2>
<button onClick={() => this.setState({ hasError: false })}>
Try again
</button>
</div>
);
}
return this.props.children;
}
}
// Usage
<ErrorBoundary fallback={<ErrorPage />}>
<UserProfile />
</ErrorBoundary>
// react-error-boundary library (hooks-based)
import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div>
<p>Error: {error.message}</p>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
<ErrorBoundary FallbackComponent={ErrorFallback} onError={logError}>
<Component />
</ErrorBoundary> Hard React Suspense and Concurrent Mode
Suspense Concurrent Mode Code Splitting Server Components Asked at: Meta Google
React Suspense and Concurrent Mode
// Suspense for code splitting
const Profile = lazy(() => import('./Profile'));
<Suspense fallback={<ProfileSkeleton />}>
<Profile userId={id} />
</Suspense>
// Suspense for data fetching (React 18+)
// With React Query:
function Posts() {
const { data } = useSuspenseQuery({ queryKey: ['posts'], queryFn: fetchPosts });
return <PostList posts={data} />; // no loading state needed!
}
<Suspense fallback={<PostsSkeleton />}>
<Posts />
</Suspense>
// Nested Suspense boundaries
<Suspense fallback={<PageSkeleton />}>
<Header />
<Suspense fallback={<FeedSkeleton />}>
<Feed />
</Suspense>
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
</Suspense>
// useTransition โ mark updates as non-urgent
function TabContainer() {
const [tab, setTab] = useState('home');
const [isPending, startTransition] = useTransition();
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab); // don't block UI during expensive tab switch
});
}
return (
<>
<TabButton onClick={() => selectTab('home')}>Home</TabButton>
<TabButton onClick={() => selectTab('analytics')}>Analytics</TabButton>
{isPending ? <Spinner /> : <TabPanel tab={tab} />}
</>
);
}
Must Revise
Medium Custom Hooks โ patterns and best practices
Custom Hooks Patterns Reusability Asked at: Meta Google Flipkart Amazon
Custom Hooks โ patterns and best practices
// Custom hooks extract stateful logic
// 1. useFetch โ data fetching
function useFetch(url) {
const [state, setState] = useState({
data: null, loading: true, error: null
});
useEffect(() => {
let cancelled = false;
setState({ data: null, loading: true, error: null });
fetch(url)
.then(r => r.json())
.then(data => {
if (!cancelled) setState({ data, loading: false, error: null });
})
.catch(error => {
if (!cancelled) setState({ data: null, loading: false, error });
});
return () => { cancelled = true; }; // cleanup โ prevents stale state
}, [url]);
return state;
}
// 2. useLocalStorage โ persisted state
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setStoredValue = useCallback((newValue) => {
setValue(newValue);
window.localStorage.setItem(key, JSON.stringify(newValue));
}, [key]);
return [value, setStoredValue];
}
// 3. useDebounce
function useDebounce(value, delay) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
// 4. useIntersectionObserver โ infinite scroll / lazy load
function useIntersectionObserver(ref, options = {}) {
const [isIntersecting, setIntersecting] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => setIntersecting(entry.isIntersecting),
options
);
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, [ref, options]);
return isIntersecting;
} Hard React Design Patterns โ HOC, Render Props, Compound Components
Design Patterns HOC Render Props Compound Components Asked at: Meta Google Adobe
React Design Patterns โ HOC, Render Props, Compound Components
// 1. Higher-Order Component (HOC)
function withAuth(Component) {
return function AuthenticatedComponent(props) {
const { user } = useAuth();
if (!user) return <Redirect to="/login" />;
return <Component {...props} user={user} />;
};
}
const ProtectedDashboard = withAuth(Dashboard);
// 2. Render Props
function Mouse({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMove = (e) => setPosition({ x: e.clientX, y: e.clientY });
return <div onMouseMove={handleMove}>{render(position)}</div>;
}
<Mouse render={({ x, y }) => <Cat x={x} y={y} />} />
// 3. Compound Components (most modern)
const Tabs = ({ children, defaultTab }) => {
const [activeTab, setActiveTab] = useState(defaultTab);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
{children}
</TabsContext.Provider>
);
};
Tabs.List = ({ children }) => <div role="tablist">{children}</div>;
Tabs.Tab = ({ id, children }) => {
const { activeTab, setActiveTab } = useContext(TabsContext);
return (
<button
role="tab"
aria-selected={activeTab === id}
onClick={() => setActiveTab(id)}>
{children}
</button>
);
};
Tabs.Panel = ({ id, children }) => {
const { activeTab } = useContext(TabsContext);
return activeTab === id ? <div role="tabpanel">{children}</div> : null;
};
// Usage โ feels like native HTML
<Tabs defaultTab="profile">
<Tabs.List>
<Tabs.Tab id="profile">Profile</Tabs.Tab>
<Tabs.Tab id="settings">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Panel id="profile"><ProfileContent /></Tabs.Panel>
<Tabs.Panel id="settings"><SettingsContent /></Tabs.Panel>
</Tabs> Hard React Server Components (RSC) โ how they work
Server Components Next.js Performance Streaming Asked at: Meta Vercel
React Server Components (RSC) โ how they work
Key Concept
Server Components run ONLY on the server, never on the client. They can directly access databases/file systems, have zero JS bundle impact, but cannot use state, effects, or browser APIs.
| Feature | Server Component | Client Component |
|---|---|---|
| Rendering | Server only | Client (+ optional SSR) |
| useState/useEffect | โ Not allowed | โ |
| Browser APIs | โ Not allowed | โ |
| Direct DB access | โ | โ |
| Bundle impact | 0 bytes JS | Adds to bundle |
| Async/await | โ | โ (use hooks) |
// Server Component (default in Next.js App Router)
// app/users/page.tsx
async function UsersPage() {
// Direct DB access! No API needed
const users = await db.query('SELECT * FROM users');
return (
<div>
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}
// Client Component โ 'use client' directive
'use client';
import { useState } from 'react';
function LikeButton({ initialLikes }) {
const [likes, setLikes] = useState(initialLikes);
return (
<button onClick={() => setLikes(l => l + 1)}>
โค๏ธ {likes}
</button>
);
}
// Mix: pass server data as props to client components
async function PostPage({ id }) {
const post = await db.posts.findById(id); // server
return (
<article>
<h1>{post.title}</h1>
<LikeButton initialLikes={post.likes} /> {/* client */}
</article>
);
}
Must Revise
Easy Controlled vs Uncontrolled Components in React
Forms Controlled Uncontrolled Refs Asked at: Amazon Microsoft Flipkart
Controlled vs Uncontrolled Components in React
| Feature | Controlled | Uncontrolled |
|---|---|---|
| State managed by | React (useState) | DOM itself |
| Value access | via state variable | via ref.current.value |
| Real-time validation | โ Easy | โ Harder |
| Syncing to other state | โ Easy | โ Harder |
| Performance | Re-render on each keystroke | No re-renders during typing |
| When to use | Most cases, complex forms | Simple forms, file inputs |
// Controlled โ React owns the value
function ControlledForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
function handleSubmit(e) {
e.preventDefault();
console.log({ name, email }); // always up to date
}
return (
<form onSubmit={handleSubmit}>
<input
value={name} // controlled: value from state
onChange={e => setName(e.target.value)}
placeholder="Name"
/>
<input
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email"
/>
<button type="submit">Submit</button>
</form>
);
}
// Uncontrolled โ DOM owns the value (use refs to access)
function UncontrolledForm() {
const nameRef = useRef(null);
const fileRef = useRef(null);
function handleSubmit(e) {
e.preventDefault();
console.log(nameRef.current.value); // read on submit
console.log(fileRef.current.files[0]); // file input always uncontrolled
}
return (
<form onSubmit={handleSubmit}>
<input ref={nameRef} defaultValue="Alice" placeholder="Name" />
<input ref={fileRef} type="file" />
<button type="submit">Submit</button>
</form>
);
}
Must Revise
Easy State vs Props โ key differences
State Props Fundamentals Data Flow Asked at: Amazon Flipkart Microsoft
State vs Props โ key differences
| Aspect | Props | State |
|---|---|---|
| Who owns it? | Parent component | Component itself |
| Mutable? | โ Read-only (from child's perspective) | โ Yes (via setState/useState) |
| Triggers re-render? | โ Yes (when parent re-renders) | โ Yes (when state changes) |
| Passed from | Parent to child | Initialized internally |
| Purpose | Configure/customize child | Track component's internal data |
function Parent() {
const [count, setCount] = useState(0); // state โ owned by Parent
return (
<Child
count={count} // prop passed to Child
onIncrement={() => setCount(c => c + 1)} // prop: callback
/>
);
}
function Child({ count, onIncrement }) { // receives props
// โ Cannot modify props: count = 5; (error or no effect)
// โ
Must call callback to request parent update
return (
<div>
<p>Count: {count}</p>
<button onClick={onIncrement}>+1</button>
</div>
);
}
// Lifting state up โ when siblings need shared state
function App() {
const [query, setQuery] = useState(''); // lifted to common ancestor
return (
<>
<SearchInput value={query} onChange={setQuery} />
<SearchResults query={query} />
</>
);
} Unidirectional data flow
Data flows DOWN (parent โ child via props). Events flow UP (child โ parent via callback props). This predictable pattern makes React apps easier to debug.
Must Revise
Hard Stale Closures in React Hooks โ the sneaky bug
Stale Closure useEffect useCallback Hooks Asked at: Meta Google Netflix
Stale Closures in React Hooks โ the sneaky bug
The Stale Closure Problem
A hook callback closes over a value at the time it was created. If the value changes but the callback is NOT recreated, it still references the OLD (stale) value.
// โ STALE CLOSURE BUG
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
// 'count' is captured at 0 (stale!)
console.log(count); // always logs 0
setCount(count + 1); // always sets to 1!
}, 1000);
return () => clearInterval(interval);
}, []); // empty deps โ callback never updates
}
// โ
Fix 1: Include dependency
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1); // fresh count each interval
}, 1000);
return () => clearInterval(interval);
}, [count]); // re-creates interval when count changes
// โ
Fix 2: Functional update (best for intervals/timers)
useEffect(() => {
const interval = setInterval(() => {
setCount(prev => prev + 1); // always gets fresh prev value
}, 1000);
return () => clearInterval(interval);
}, []); // no dependency needed!
// โ
Fix 3: useRef to always have fresh value
const countRef = useRef(count);
useEffect(() => { countRef.current = count; }, [count]);
useEffect(() => {
const interval = setInterval(() => {
console.log(countRef.current); // always fresh!
}, 1000);
return () => clearInterval(interval);
}, []); eslint-plugin-react-hooks
The exhaustive-deps ESLint rule catches stale closures by warning when you forget to include dependencies. Always heed its warnings!
Must Revise
Hard Redux โ actions, reducers, store, middleware
Redux State Management Middleware Thunk Asked at: Meta Amazon Flipkart Netflix
Redux โ actions, reducers, store, middleware
User Action
โ
โผ
โโโโโโโโโโโโโโโโ
โ Action โ { type: 'INCREMENT', payload: 1 }
โ Creator โ
โโโโโโโโฌโโโโโโโโ
โ dispatch(action)
โผ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Middleware โโโโโโบโ Async effects โ
โ (thunk/ โ โ (API calls) โ
โ saga) โ โโโโโโโโโโโโโโโโโโโ
โโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโ
โ Reducer โ (prevState, action) => newState
โ (pure fn) โ never mutates โ returns new object
โโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโ
โ Store โ Single source of truth
โโโโโโโโฌโโโโโโโโ
โ triggers
โผ
โโโโโโโโโโโโโโโโ
โ React re- โ useSelector reads new state
โ renders โ
โโโโโโโโโโโโโโโโ
// Modern Redux Toolkit (RTK) โ recommended approach
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
// Slice โ actions + reducer in one
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0, status: 'idle' },
reducers: {
increment: (state) => { state.value += 1; }, // Immer allows "mutation"
decrement: (state) => { state.value -= 1; },
incrementBy: (state, action) => { state.value += action.payload; },
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => { state.status = 'loading'; })
.addCase(fetchUser.fulfilled, (state, action) => {
state.status = 'idle';
state.user = action.payload;
});
}
});
// Async thunk โ for API calls
const fetchUser = createAsyncThunk('user/fetch', async (id) => {
const res = await fetch(`/api/users/${id}`);
return res.json();
});
// In component
import { useSelector, useDispatch } from 'react-redux';
function Counter() {
const count = useSelector(state => state.counter.value);
const dispatch = useDispatch();
return (
<>
<p>{count}</p>
<button onClick={() => dispatch(counterSlice.actions.increment())}>+</button>
<button onClick={() => dispatch(fetchUser(1))}>Fetch User</button>
</>
);
}