0% found this document useful (0 votes)
11 views24 pages

Advanced React Handbook

The Advanced React Handbook covers intermediate to advanced concepts in React, including hooks, custom hooks, context API, error boundaries, code splitting, and concurrent features. It provides detailed explanations and examples of how to optimize React applications using various hooks like useEffect, useMemo, and useCallback. Additionally, it discusses best practices for performance and state management, along with new features introduced in React 18.

Uploaded by

Aahan piplani
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views24 pages

Advanced React Handbook

The Advanced React Handbook covers intermediate to advanced concepts in React, including hooks, custom hooks, context API, error boundaries, code splitting, and concurrent features. It provides detailed explanations and examples of how to optimize React applications using various hooks like useEffect, useMemo, and useCallback. Additionally, it discusses best practices for performance and state management, along with new features introduced in React 18.

Uploaded by

Aahan piplani
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Advanced React Handbook

This handbook covers intermediate to advanced React concepts, integrating related technologies and
best practices. It includes detailed explanations, code examples, diagrams, and tables to reinforce
learning. Citations refer to authoritative sources to ensure accuracy and up-to-date information.

React Hooks and Functional Patterns


React hooks enable functional components to manage state and side effects. Understanding advanced
hooks is crucial for optimizing and structuring React apps.

useEffect and Side Effects

The useEffect hook runs after render, letting you perform side effects (data fetching, subscriptions,
etc.). It optionally takes a dependency array to control when it re-runs:

import { useState, useEffect } from 'react';

function DataFetcher({ id }) {
const [data, setData] = useState(null);

useEffect(() => {
// Side effect: fetch data when component mounts or id changes
fetch(`/api/data/${id}`)
.then(res => [Link]())
.then(json => setData(json))
.catch(err => [Link](err));

// Optional cleanup (e.g., abort fetch on unmount):


// return () => [Link]();
}, [id]); // effect runs only when `id` changes

return <div>{data ? [Link] : 'Loading...'}</div>;


}

• Dependency array: Passing [id] means the effect runs on mount and whenever id
changes. An empty array [] runs only once after the first render. Omitting it runs the effect
after every render (often undesirable).
• Cleanup: If your effect returns a function, React calls it before unmounting or before re-running
the effect. This is useful to cancel subscriptions or timers.

Common pitfalls include missing dependencies (causing stale values) or including too many (causing
infinite loops). Always list all state/props used in the effect dependencies to ensure correctness 1 2 .

1
useMemo and useCallback

React provides useMemo and useCallback to memoize values and functions between renders. This
avoids re-computing expensive calculations or re-creating functions on each render.

• useMemo(() => value, [deps]) : Caches a computed value until dependencies change.
• useCallback(fn, [deps]) : Caches the function fn (same reference) until dependencies
change.

For example, if an expensive computation depends on a prop, wrap it in useMemo :

import React, { useMemo } from 'react';

function ExpensiveComponent({ items }) {


// Only recompute when `items` changes
const sortedItems = useMemo(() => {
[Link]('Sorting items');
return [...items].sort();
}, [items]);

return <List items={sortedItems} />;


}

Without useMemo , sortedItems would be recomputed on every render, even if items is the same.
useCallback is similar but for functions:

import React, { useState, useCallback } from 'react';

function Counter() {
const [count, setCount] = useState(0);

// This function is recreated only when `count` changes


const increment = useCallback(() => {
setCount(c => c + 1);
}, [setCount]);

return <button onClick={increment}>Count: {count}</button>;


}

Optimization note: useMemo and useCallback introduce their own overhead. Use
them when there is a performance issue (e.g. expensive computation or large component
trees) 3 4 . Overusing memoization can complicate code without benefit.

Custom Hooks

Custom hooks encapsulate reusable logic. They are functions named with use prefix that call built-in
hooks. For example, a hook for window size:

2
import { useState, useEffect } from 'react';

function useWindowWidth() {
const [width, setWidth] = useState([Link]);
useEffect(() => {
const handleResize = () => setWidth([Link]);
[Link]('resize', handleResize);
return () => [Link]('resize', handleResize);
}, []);
return width;
}

function MyComponent() {
const width = useWindowWidth();
return <div>Window width: {width}</div>;
}

Custom hooks allow composition of logic across components and improve code organization. They
follow the Rules of Hooks (call hooks only at top level and from React components or other hooks) 5 .

Refs and forwardRef

The useRef hook creates a mutable ref object with a .current property. It can hold any value and
persists across renders without causing updates.

• DOM refs: Attach a ref to a DOM element to access it:

import { useRef } from 'react';

function TextInputWithFocus() {
const inputRef = useRef(null);
return (
<div>
<input ref={inputRef} />
<button onClick={() => [Link]()}>
Focus the input
</button>
</div>
);
}

• Instance-like refs: Use refs to hold mutable values (timers, previous props) that don’t trigger re-
renders. For example, to store a previous value:

function usePrevious(value) {
const ref = useRef();
useEffect(() => { [Link] = value; });

3
return [Link];
}

[Link] allows a component to accept a ref prop and forward it to a child. This is
useful when a parent needs to call imperative methods on a child’s DOM or expose internal functions.
For example:

import React, { forwardRef, useImperativeHandle, useRef } from 'react';

const FancyInput = forwardRef((props, ref) => {


const inputRef = useRef();

// Expose focus() to parent via ref


useImperativeHandle(ref, () => ({
focus: () => {
[Link]();
}
}));

return <input ref={inputRef} {...props} />;


});

function Parent() {
const fancyRef = useRef();
return (
<div>
<FancyInput ref={fancyRef} />
<button onClick={() => [Link]()}>
Focus FancyInput
</button>
</div>
);
}

React’s documentation notes that [Link] “returns a component that can accept a ref
prop and forward it to one of its children” 6 , enabling this pattern.

Additional Hooks

• useLayoutEffect : Similar to useEffect but fires synchronously after all DOM mutations.
Use it to measure layout before the browser paints.
• useImperativeHandle : As shown above, customizes the instance value exposed by a ref.
• State Hooks ( useState , useReducer ): While useState is basic, useReducer can
manage complex state logic (akin to Redux reducers) for predictable updates.

Best practices: Always place hooks at the top level of your component. Do not call hooks inside loops or
conditions, to maintain consistent order across renders 7 .

4
Context API and State Sharing
React’s Context API provides a way to share values (like theme, user, settings) across the component
tree without prop drilling. Create a context and wrap components in a Provider:

const ThemeContext = [Link]('light');

function App() {
return (
<[Link] value="dark">
<Toolbar />
</[Link]>
);
}

function Toolbar() {
const theme = useContext(ThemeContext);
return <div className={theme}>Toolbar</div>;
}

Performance and Optimization

By default, any change to the value prop of a Provider triggers all consuming components to re-
render, even if they do not use the changed part. For example:

function App() {
const [count, setCount] = useState(0);
const theme = { color: 'blue' }; // new object each render
return (
<[Link] value={theme}>
<button onClick={() => setCount(c => c+1)}>Increment {count}</button>
<ChildComponent />
</[Link]>
);
}

Here, theme is a new object on each render, so all Context consumers re-render on any state change.
To optimize:

• Memoize context value: Wrap complex values in useMemo so the provider only changes when
needed 8 :

const theme = useMemo(() => ({ color: themeColor }), [themeColor]);


<[Link] value={theme}>...</[Link]>

• Split contexts: Use multiple contexts if different parts of state can change independently, so
unrelated components only subscribe to what they need 9 .

5
• [Link] : For consumer components, wrap them with [Link] so they skip renders
when context value identity hasn’t changed (useful if context value rarely changes).

For example: As one source explains, “If you pass an object or array as value to a Context
Provider, a new reference is created on each render. Even if the actual data didn’t change,
consumers will re-render because the reference changed. Using useMemo to memoize the
context value prevents this unnecessary re-rendering.” 8 .

Example: Theming with Context

const ThemeContext = [Link]({ color: 'light' });

function App() {
const [darkMode, setDarkMode] = useState(false);
// Memoize to avoid passing a new object each time
const theme = useMemo(() => ({ color: darkMode ? 'dark' : 'light' }),
[darkMode]);

return (
<[Link] value={theme}>
<button onClick={() => setDarkMode(d => !d)}>
Toggle Dark Mode
</button>
<ThemedBox />
</[Link]>
);
}

const ThemedBox = [Link](() => {


const theme = useContext(ThemeContext);
// Only re-renders when `[Link]` changes
return <div className={[Link]}>I am a {[Link]} box</div>;
});

By combining useMemo and [Link] , this example avoids redundant renders of ThemedBox
when unrelated state changes. Proper use of context and memoization is key to performance when
sharing state globally.

Error Boundaries
React error boundaries catch exceptions in child components and display a fallback UI instead of
crashing the whole app 10 . Only class components can be error boundaries (as of React 18).

class ErrorBoundary extends [Link] {


constructor(props) {
super(props);
[Link] = { hasError: false };
}
static getDerivedStateFromError(error) {

6
// Update state to trigger fallback render
return { hasError: true };
}
componentDidCatch(error, info) {
// Log error (optional)
[Link]('ErrorBoundary caught:', error, info);
}
render() {
if ([Link]) {
return <h2>Something went wrong.</h2>;
}
return [Link];
}
}

Use it by wrapping components:

<ErrorBoundary>
<Widget /> {/* Errors in Widget will be caught */}
</ErrorBoundary>

Key points: - Error boundaries catch errors during rendering, in lifecycle methods, and constructors
of children 10 .
- They do not catch errors inside event handlers, asynchronous code (e.g. setTimeout), or in themselves
11 .

- If an error is not caught by any boundary, React unmounts the entire component tree (worse than
showing broken UI) 12 . - Typically, wrap high-level routes or major UI sections with an error boundary
to isolate crashes.

Error boundaries are akin to a try/catch for React components, preserving app stability.

Code Splitting and Lazy Loading


Large React apps benefit from code splitting, where the bundle is divided so only needed code is
loaded. React supports this via dynamic import() and [Link] .

• Dynamic import() : Bundlers (like Webpack) see import('./MyComponent') and


automatically split that code into a separate chunk 13 .
• [Link] : Turns a dynamic import into a component that is loaded on-demand:

import React, { Suspense } from 'react';

const LazyComponent = [Link](() => import('./HeavyComponent'));

function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />

7
</Suspense>
);
}

Here, HeavyComponent is only fetched when LazyComponent is rendered. The <Suspense>


boundary shows the fallback (like a spinner) while loading 13 .

Benefits: Faster initial load because code for off-screen parts is not loaded upfront.
Notes: - Only default exports can be lazy-loaded. - Always wrap lazy components in <Suspense> with
a fallback UI 13 . - You can nest <Suspense> boundaries to coordinate multiple lazy loads.

For route-based splitting, combine React Router with [Link] , loading each route’s component
only when needed. This significantly reduces the initial bundle size.

Portals
A portal allows rendering a React subtree into a DOM node outside the parent component’s DOM
hierarchy 14 . This is useful for elements like modals, tooltips, or dropdowns that should break out of
overflow or stacking contexts.

import { createPortal } from 'react-dom';

function Modal({ children }) {


return createPortal(
<div className="modal">
{children}
</div>,
[Link]('modal-root')
);
}

// Usage in App
<div>
<h1>Main App</h1>
<Modal>
<p>This is rendered into #modal-root in the DOM.</p>
</Modal>
</div>

Under the hood, createPortal(children, domNode) returns a React element that is inserted into
the specified domNode 15 . From React’s perspective, the portal content is still part of the parent
component’s tree (so context and event bubbling work normally) 16 17 .

For example, rendering a modal via a portal ensures it visually appears above all other content, even if
the parent’s CSS has overflow: hidden . Events propagate through the React tree, not the DOM
tree, so parent event handlers still work 16 18 .

8
Suspense and Concurrent Features (React 18+)
React 18 introduced new concurrency primitives and expanded Suspense beyond code-splitting:

• Suspense for Data Fetching (experimental): In future React versions or with specific libraries,
you can have components suspend rendering until data is ready. (This leverages <Suspense>
to show fallback content while waiting for data.) For now, libraries like Relay or React Server
Components use this pattern.
• useTransition Hook: Marks certain state updates as non-urgent (concurrent).
useTransition returns [isPending, startTransition] . Wrap a state update in
startTransition(() => { setState(...) }) to hint that this update can be interrupted
to keep the UI responsive 19 20 . While transitioning, isPending is true , letting you show a
pending indicator.

import { useState, useTransition } from 'react';

function Search() {
const [isPending, startTransition] = useTransition();
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);

const handleChange = (e) => {


setQuery([Link]);
startTransition(() => {
fetch(`/search?q=${[Link]}`)
.then(res => [Link]())
.then(json => setResults([Link]));
});
};

return (
<div>
<input value={query} onChange={handleChange} />
{isPending ? <p>Searching...</p> : <ResultList items={results} />}
</div>
);
}

Updates inside startTransition are interruptible—if a user types quickly, React may pause the fetch
update to process the latest keystrokes first. The isPending flag can show loading status.
useTransition is ideal for smoothing out input-heavy UIs 19 20 .

• useDeferredValue Hook: When a value changes rapidly (like user input),


useDeferredValue lets you render an old value while updating the new one in the
background 21 . This prevents the UI from janking during heavy re-renders. For example:

function SearchResults({ query }) {


const deferredQuery = useDeferredValue(query);

9
// Use deferredQuery to fetch or filter results
}

Here, if query changes often, deferredQuery lags behind slightly. React will first render with the
old value ( deferredQuery ), then start a background re-render with the new value once it’s available
21 . If the deferred render suspends (e.g. awaiting data), the user still sees the old results until the new
data is ready 22 .

• React Concurrent Mode: Enabled by default in React 18 when using createRoot . It includes
automatic batching and the above hooks. Concurrent Mode can make rendering non-blocking,
improving perceived performance for high-latency operations 23 24 .

Diagram: A typical visualization of React’s virtual DOM is shown below, illustrating how React keeps an
in-memory tree to efficiently update the real DOM:

Illustration of React’s “virtual DOM” concept: React maintains a lightweight copy of the UI (virtual DOM) to
batch and minimize updates to the real DOM 25 .

Performance Profiling and Optimization

Profiling

Use the React DevTools Profiler to identify slow components. The Profiler tab records render timings
and highlights costly renders. Programmatically, React offers a <Profiler> component that wraps a
tree and calls an onRender callback on every commit 26 :

import { Profiler } from 'react';

function onRenderCallback(
id, // Profiler id
phase, // "mount" or "update"
actualDuration, // time spent rendering

10
baseDuration // estimated time if no memoization
) {
[Link](`${id} rendered in ${actualDuration}ms (base ${baseDuration}
ms)`);
}

<Profiler id="App" onRender={onRenderCallback}>


<App />
</Profiler>

This can log how long rendering took, useful in custom performance tests. The callback provides
actualDuration (time including optimizations like memo) and baseDuration (time if no optimizations),
enabling you to gauge how well your memoization works 2 .

Common Optimization Techniques

• Production Build: Always test performance in a production build ( npm run build ). Dev builds
include extra checks and are slower 27 .
• [Link] / PureComponent : Wrap functional components with [Link] to skip re-
renders when props are shallowly equal. Class components can extend
[Link] for similar effect 28 29 . This prevents unnecessary reconciliation of
subtrees.
• Keys in Lists: Always give list items stable, unique keys (not array index) to help React match
elements between renders. Correct keys enable React’s diff algorithm to minimize updates, while
wrong keys can cause full re-renders or bugs 30 .
• Avoid Anonymous Functions and Objects in Props: In parent components, avoid re-creating
functions or objects inline on every render (e.g.
<Child onClick={() => doSomething()} /> ). Use useCallback or useMemo to
stabilize these props; otherwise, child components will see new prop references each time and
re-render.
• Avoid Frequent State Updates: Batch state updates and avoid setting state in tight loops. Use
functional updates ( setState(prev => new) ) to enqueue updates efficiently.
• Virtualization: For large lists, use windowing libraries (e.g. react-window) so only visible items
are rendered. This saves rendering time and DOM nodes.
• Dependency Arrays: Define hook dependencies precisely to avoid redundant effect calls. As one
performance guide states, “Always define dependency arrays properly in hooks like useEffect or
useCallback . Incorrect dependencies can lead to effects running too often or not updating when
they should” 4 .
• Memoize Context Values: As discussed, wrap context provider values with useMemo to avoid
unwanted renders 8 .

Example: Memoizing Expensive Component

const ExpensiveComponent = [Link](function({ data }) {


[Link]('ExpensiveComponent render');
// ... expensive rendering
return <div>{data}</div>;
});

function App() {

11
const [count, setCount] = useState(0);
const data = 'static data';
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<ExpensiveComponent data={data} />
</div>
);
}

Here, ExpensiveComponent will not re-render when count changes, because data prop is
unchanged and wrapped in [Link] 31 . The console log helps verify that.

Key Takeaways

1. Measure First: Use profiling tools (React Profiler, browser performance) to identify actual
bottlenecks.
2. Memoize Wisely: Apply [Link] , useMemo , and useCallback when there is
demonstrated benefit 2 4 . These prevent unnecessary re-renders but add complexity.
3. Correct Dependencies: Always specify hook dependencies accurately to avoid stale closures or
excess updates 4 .
4. Code Split and Lazy Load: Use lazy loading for components not needed at startup to shrink
initial bundle 13 4 .
5. Iterate: Performance tuning is iterative – optimize, test, and refine.

React Router v6
React Router enables client-side navigation in single-page apps. Version 6 introduced a simplified API
and powerful features 32 :

• <Routes> replaces <Switch> for route matching. Each <Route> uses an element prop
for the component.
• Nested routes with <Outlet> , letting child routes render inside parent layouts.
• Hooks: useNavigate (programmatic navigation), useParams (URL parameters),
useLocation , and useMatch .
• Route-based code splitting: Combined with [Link] , you can lazy-load route
components for each path.
• New data APIs (v6.4+): Route loaders and actions for fetching data on navigation, plus
integration with error boundaries for routes.

Example usage:

import { BrowserRouter, Routes, Route, useNavigate } from 'react-router-dom';

function Home() {
const navigate = useNavigate();
return <button onClick={() => navigate('/about')}>Go to About</button>;
}

function App() {

12
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="users/:id" element={<UserProfile />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}

This setup enables seamless navigation without full page reloads, leveraging the History API 32 .
The image below illustrates the SPA concept of navigating between “pages” (browser routes) without
refreshing the browser:

Client-side routing in React (e.g. with React Router) enables seamless navigation without reloading the page
32 .

In React Router v6, nested routes and layout routes are easy to define using <Routes> and
<Outlet> . For example:

<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Dashboard />} />
<Route path="settings" element={<Settings />} />
<Route path="users">
<Route index element={<UserList />} />
<Route path=":id" element={<UserProfile />} />
</Route>
</Route>
</Routes>

13
Here, <Layout> contains an <Outlet /> where child routes render. The /users path has nested
routes for listing and detail pages.

Redux and Advanced State Management


While React’s Context can share state, larger apps often use Redux or similar libraries for complex state
logic. We cover Redux, middleware, and the Redux Toolkit.

Redux Basics

Redux centralizes state in a single store, updated via actions and reducers. In React, use the react-
redux library with <Provider> to give components access to the store, and use useSelector /
useDispatch hooks (or connect HOC).

import { Provider } from 'react-redux';


import store from './store';
function App() {
return (
<Provider store={store}>
<MyAppComponents />
</Provider>
);
}

A reducer is a function (state, action) => newState . Actions are plain objects { type,
payload } . dispatch sends actions to the store.

Redux Thunk and Async Actions

Redux Thunk is a middleware that allows dispatching functions (thunks) for async logic instead of plain
objects. With Thunk, an action creator can return a function that receives (dispatch, getState) :

// Thunk action creator


function fetchUser(userId) {
return async (dispatch) => {
dispatch({ type: 'user/fetchStart' });
try {
const res = await fetch(`/api/users/${userId}`);
const data = await [Link]();
dispatch({ type: 'user/fetchSuccess', payload: data });
} catch (err) {
dispatch({ type: 'user/fetchError', error: err });
}
};
}

Dispatching fetchUser(123) will run this function. Thunks help handle side effects without tying
components directly to async logic.

14
Redux Saga

Redux Saga is an alternative async middleware using ES6 generators. It listens for actions and runs
“saga” generator functions to perform side effects. For example, a watcher saga:

import { takeLatest, call, put } from 'redux-saga/effects';

function* fetchUserSaga(action) {
try {
const data = yield call([Link], [Link]);
yield put({ type: 'user/fetchSuccess', payload: data });
} catch (e) {
yield put({ type: 'user/fetchError', error: e });
}
}

function* watchFetchUser() {
yield takeLatest('user/fetchRequest', fetchUserSaga);
}

Here, dispatching {type: 'user/fetchRequest', payload: userId} triggers


fetchUserSaga . Redux Saga makes async flows easier to test and avoids “callback hell” by leveraging
generator yield 33 . A blogger notes “Redux-saga is...designed to make handling side effects nice and
simple. It achieves this by leveraging ES6 generators, allowing us to write asynchronous code that looks
synchronous and is very easy to test.” 33 .

Redux Toolkit

Redux Toolkit (RTK) is the official, opinionated Redux library that simplifies setup and common tasks.
Key utilities:

• configureStore() : Sets up the store with good defaults (Thunk middleware, Redux DevTools,
etc.) 34 .
• createSlice({ name, initialState, reducers }) : Auto-generates action creators and
action types for a given slice of state 35 .
• createAsyncThunk : Simplifies writing thunks for async logic, automatically dispatching
pending/fulfilled/rejected actions 36 .
• Other utilities: createEntityAdapter , createSelector , etc.

For example, a slice for users:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

export const fetchUsers = createAsyncThunk(


'users/fetchAll',
async () => {
const res = await fetch('/api/users');
return [Link]();
}

15
);

const usersSlice = createSlice({


name: 'users',
initialState: { list: [], status: 'idle', error: null },
reducers: {
userAdded(state, action) {
[Link]([Link]);
}
},
extraReducers: (builder) => {
builder
.addCase([Link], (state) => { [Link] = 'loading'; })
.addCase([Link], (state, action) => {
[Link] = 'succeeded';
[Link] = [Link];
})
.addCase([Link], (state, action) => {
[Link] = 'failed';
[Link] = [Link];
});
}
});

export default [Link];

By using RTK, boilerplate is reduced: action types and creators are auto-generated 37 , and the store
setup is concise:

import { configureStore } from '@reduxjs/toolkit';


import usersReducer from './usersSlice';

const store = configureStore({


reducer: {
users: usersReducer,
// add other slice reducers here
}
});

RTK includes redux-thunk by default 34 (so you can dispatch async thunks). It also warns against
common mistakes (e.g. mutating state) in development by default.

Server-Side Rendering and [Link]


[Link] is a React framework that supports SSR (Server-Side Rendering) and SSG (Static Site
Generation) out of the box, enhancing performance and SEO.

• CSR (Client-Side Rendering): React single-page apps load a minimal HTML and fetch data on the
client.

16
• SSR with [Link]: A page’s HTML is generated on each request by the server. In [Link], you
export an async function getServerSideProps(context) from a page. [Link] runs it on the
server at request time, passes props to the React component, and sends fully-rendered HTML to
the client 38 . Example:

// pages/[Link]
export async function getServerSideProps(context) {
const res = await fetch('[Link]
const profile = await [Link]();
return { props: { profile } };
}
export default function Profile({ profile }) {
return <div>Name: {[Link]}</div>;
}

This fetches data on the server every time. getServerSideProps runs only on the server and never
in the browser 39 . It is ideal for data that changes per request (user-specific data, auth info) 40 .

• SSG (Static Site Generation): Pages are generated at build time. Export
getStaticProps(context) , and [Link] will pre-render the page into static HTML using its
returned props 41 . This HTML and a JSON of props are served to clients. Example:

// pages/[Link]
export async function getStaticProps() {
const res = await fetch('[Link]
const posts = await [Link]();
return { props: { posts } };
}
export default function Posts({ posts }) {
return (
<ul>
{[Link](post => <li key={[Link]}>{[Link]}</li>)}
</ul>
);
}

With SSG, content is generated once (or regenerated in the background if using Incremental Static
Regeneration). Use SSG when data is available at build time and can be cached (e.g. public blog posts)
42 . The static page loads very fast via CDN caching.

• Comparison:

Rendering Method When HTML is generated Example Use Case

CSR (React SPA) In browser, after load User dashboard behind login, frequent updates

SSR On server per request Personalized pages (profiles, user data) 43

SSG At build time (static) Marketing pages, blogs (fast, SEO) 42

ISR ([Link]) Pre-render + revalidate Mostly static pages with occasional updates

17
[Link] abstracts most configuration. Using getServerSideProps vs getStaticProps depends on
data needs. In both cases, [Link] hydrates the React app on the client (initial HTML is interactive).
Remember to not send sensitive data via props , as they are viewable in the client HTML 39 44 .

Testing React Applications


Testing ensures reliability. Two popular tools are Jest (test runner/assertions) and React Testing
Library (RTL) for React components.

Jest Snapshot Testing

Jest allows snapshot tests: it renders a component and saves its output as a snapshot. Future runs
compare against this snapshot to detect unexpected changes 45 .

import renderer from 'react-test-renderer';


import MyComponent from './MyComponent';

it('renders correctly', () => {


const tree = [Link](<MyComponent label="Hello" />).toJSON();
expect(tree).toMatchSnapshot();
});

The first run writes a snapshot file containing the component’s rendered tree. On changes, tests fail if
the output no longer matches the snapshot 45 . This catches unintended UI changes, but be cautious:
snapshots should be reviewed before updating them. As the Jest docs say, “Snapshot tests are useful
whenever you want to make sure your UI does not change unexpectedly” 45 .

React Testing Library (RTL)

React Testing Library focuses on testing components via the user’s perspective 46 . It encourages
querying DOM elements by text, role, or label, rather than implementation details like component
internals.

import { render, screen } from '@testing-library/react';


import userEvent from '@testing-library/user-event';

test('increments counter', () => {


render(<Counter />);
const button = [Link]('button', { name: /increment/i });
[Link](button);
expect([Link]('Count: 1')).toBeInTheDocument();
});

Key RTL best practices 46 : - Test behavior, not implementation: Find elements like a user would (by
role or label) instead of relying on test IDs or internal state.
- Use screen : It provides queries without needing to destructure from render , making tests
cleaner.
- userEvent over fireEvent : It simulates real user interactions (typing, clicking) more closely 47 .

18
- Async testing: For async UI (API calls), use findBy (returns a Promise) or waitFor to await
changes 48 .
- Organize with describe blocks**: Group related tests for readability.

Example of testing an async fetch component:

test('loads and displays greeting', async () => {


render(<FetchComponent url="/greeting" />);
[Link]([Link]('Load Greeting'));
// findByRole waits for a heading to appear
expect(await [Link]('heading')).toHaveTextContent('Hello
there');
});

RTL works well with Mock Service Worker (MSW) or Jest mocks for API requests instead of stubbing
internals.

Jest + RTL Snapshot vs DOM Tests

Combine both: use RTL to assert user-visible behavior, and snapshots via Jest for output. For example,
snapshot-test a component with fixed props, and write interactive tests for user flows.

Example Table: Testing Tools Overview

Tool Purpose Usage

Unit tests, mocking modules, snapshot


Jest Test runner and assertions
testing 45 .

React Testing React component testing Render components, query by DOM,


Library (DOM-focused) simulate user events 46 .

(Deprecated) shallow render Was popular for shallow/mount, now less


Enzyme
components used.

Cypress / E2E testing (browser Testing full application flows in browser


Playwright automation) environment.

Ultimately, tests should be maintainable and reflect user behavior, catching regressions without
being overly tied to implementation details.

Best Practices and Design Patterns

Component Design

• Small, Focused Components: Each component should have one responsibility. This improves
reusability and testability.
• Container/Presentational Pattern: Separate “container” components (handle state, logic) from
“presentational” ones (focus on rendering UI). This clarifies concerns and makes UI components
reusable with different data.

19
• Hooks & Composition: Prefer composition and hooks over patterns like higher-order
components (HOCs) when possible. Hooks naturally compose logic (e.g. custom hooks) 49 . Use
HOCs or render props for cross-cutting concerns if needed.
• Avoid Mutation: Never mutate state or props directly. Instead, return new objects/arrays.
Immutable updates ensure correct re-rendering (especially with PureComponent or
[Link] ) 50 .

Code and Folder Organization

• File Naming: Use clear names (e.g. [Link] for components, [Link] for
hooks).
• Directory Structure: Group files by feature or type (e.g. all user related files in one folder).
Avoid deep nesting.
• Index Files: Use [Link] to re-export related modules for cleaner imports (e.g. import
{ Button } from './components' ).

Performance Tips

• Use lazy loading for heavy components and images (e.g. [Link], <img
loading="lazy"> ).
• Key Prop: Always assign stable, unique keys in lists to help React diff efficiently 30 .
• Avoid Re-Renders: Memoize expensive functions/data and use [Link] to prevent
unnecessary child renders 31 .
• Batching Updates: React batches multiple setState calls in event handlers by default. In
React 18+, updates are batched even in async callbacks.
• Profile Regularly: Keep an eye on performance as features are added. Use Lighthouse and
Profiler in development.

Common Patterns

• Render Props: A pattern where a component accepts a function as a child to customize


rendering. (Less common with hooks available, but still used in e.g. <Route> .)
• Error Boundaries: Use at top-level route components or around widgets to isolate crashes.
• Portal Use: For modals, popovers, tooltips so they aren't clipped by parent CSS.

Security Tips

• XSS Prevention: Avoid using dangerouslySetInnerHTML with untrusted content. Always


sanitize inputs if injecting HTML.
• CORS: When fetching data from APIs, configure CORS headers properly on the server.
• Dependency Updates: Regularly update React and libraries to patch vulnerabilities.

React Interview Questions


This section provides example intermediate-to-advanced interview questions with answers to test your
understanding. Each answer explains the concept and context.

Q1: What is the difference between useEffect and useLayoutEffect ?


A: Both are hooks for side effects. useEffect runs asynchronously after the browser has painted, so
it doesn't block the UI. useLayoutEffect fires synchronously after DOM updates but before
painting. Use useLayoutEffect for reading layout (like measuring DOM nodes) or applying changes

20
that must happen before the browser repaints. Misusing useLayoutEffect can block rendering, so
default to useEffect unless needed.

Q2: How do useMemo and useCallback differ, and when would you use them?
A: useMemo memoizes a value returned by a computation; useCallback memoizes a function
reference. They both take dependencies. Use useMemo to avoid re-running expensive calculations on
each render. Use useCallback to prevent child components receiving new function props
unnecessarily. For example, passing a callback to a [Link] child should use useCallback so
the child doesn’t re-render because the function changed.

Q3: How can context usage hurt performance, and how would you mitigate it?
A: Every time a context’s value changes, all consuming components re-render. If value is a new
object each render, this triggers many updates. Mitigation techniques include: memoizing the context
value with useMemo , splitting context into multiple contexts for unrelated data, and wrapping
consumers in [Link] . For example, wrap context providers around only the parts of the tree
that need them.

Q4: Explain React portals. Why use them?


A: Portals allow rendering a component’s children into a DOM node outside the parent’s hierarchy 14 .
Use them for UI elements that need to escape overflow or stacking contexts, like modals or tooltips. A
portal “teleports” its children in the DOM while preserving React context and event bubbling 16 .

Q5: What are error boundaries? Can hooks implement them?


A: Error boundaries are class components with componentDidCatch or
getDerivedStateFromError that catch render-time errors in their child tree 51 . They allow
displaying a fallback UI when a subtree crashes. Hooks cannot create error boundaries because error
boundaries rely on lifecycle methods only available in classes. You must use a class component to
implement one.

Q6: How does code splitting with [Link] and Suspense work?
A: [Link] lets you dynamically import a component, creating a separate bundle for it 13 . When
the lazy component is rendered, React loads that bundle on-demand. You must wrap lazy components
in <Suspense fallback={...}> . The fallback UI (like a loader) displays while the component is
downloading 13 . This defers loading code not needed immediately, improving initial load performance.

Q7: What is the React Profiler and what information does it give you?
A: The React Profiler (in DevTools and via the <Profiler> API) measures render timings. It tells you
how long each component took to render and how often. The <Profiler> onRender callback
provides actualDuration (render time with memoization) and baseDuration (estimated time
without memo) 2 . This helps identify slow components and verify memo optimizations.

Q8: How do you implement routing in React without a library? Why use React Router instead?
A: Without a router, you can use the History API ( pushState , onpopstate ) and render components
based on [Link] . However, React Router v6 provides a robust solution: it manages URL
parsing, nested routes, parameters, and integrates hooks (like useNavigate ) 32 . It simplifies routing
code, handles edge cases, and supports features like code-splitting by routes. The embedded router
ensures navigation is seamless (no reload) 32 .

Q9: What are the differences between getServerSideProps and getStaticProps in [Link]?
A: getServerSideProps runs on every request on the server, generating a fresh HTML page each

21
time 38 . Use it for data that changes per request (auth, personalized content). getStaticProps runs
at build time (or on a revalidate schedule) and produces static HTML 41 . It’s ideal for public, cacheable
data (blogs, docs). The trade-off is freshness vs performance.

Q10: How would you test a component that fetches data?


A: Use Jest and RTL. Mock the network call (with [Link] or MSW). Render the component, then
use findBy* queries to wait for async content. For example:

test('displays data after fetch', async () => {


[Link]( // mock API
[Link]('/items', (req, res, ctx) => res([Link]([{id:1,
name:'Item'}])))
);
render(<FetchItems />);
expect(await [Link]('Item')).toBeInTheDocument();
});

This waits for the element containing “Item” to appear after the (mocked) fetch, asserting correct
behavior. Use findBy to handle the async delay 48 .

Q11: What is the purpose of keys in React lists, and why shouldn’t you use array indices as keys?
A: Keys uniquely identify elements in a list for React’s reconciliation. Stable keys help React match old
and new items, minimizing DOM updates. Using array indices can lead to bugs when the list order
changes: React may think elements haven’t changed (since the index-based key stays same) and fail to
update properly 52 . Always use unique IDs if possible.

Q12: Describe the useTransition hook and give a use-case.


A: useTransition (React 18+) creates a transition boundary. It returns [isPending,
startTransition] . Wrapping state updates in startTransition marks them as non-urgent, so
React can interrupt them if higher-priority updates (like typing in an input) occur 19 . Use-case: a search
input that triggers a filter update on a large list. Wrapping the filter update in startTransition
keeps the typing input responsive, showing a pending state if needed.

Q13: What are React Portals useful for?


A: As mentioned, portals let parts of your component tree render outside the parent’s DOM hierarchy
14. They’re useful for UI that must overlay other content (modals, tooltips) without being clipped by
parent styles. A portal’s children still participate in React context and event bubbling normally 16 .

Q14: How does React’s reconciliation algorithm work?


A: React keeps a virtual DOM (an internal UI tree) and updates it on state/prop changes 25 . It then diffs
the new virtual DOM against the old one to compute minimal changes to apply to the actual browser
DOM. Optimizations like keys and PureComponent help reconciliation skip branches if data is
unchanged 25 28 .

Q15: What is Concurrent Rendering, and how does it affect app behavior?
A: Concurrent Rendering (React 18) allows React to interrupt and pause rendering work for better
responsiveness. For example, if the app is busy rendering a large component, concurrent mode can
yield to user input (e.g. a click) and resume rendering later. It also enables transitions and improved
batching. From the developer’s view, you may notice updates appearing out-of-order if wrapped in

22
transitions, but the UI remains interactive. Learning resources explain it as giving the browser back
control between rendering slices 53 .

These questions exemplify the reasoning and explanations expected in a technical interview for a React
developer, with a focus on understanding concepts and their practical use.

Sources: This handbook synthesized content from React’s official documentation and expert articles 3
10 13 16 21 32 38 54 46 45 34 33 , among others, to ensure accuracy and currency. Each

section includes code examples and references for deeper reading.

1 4 31 How to Profile and Optimize React Performance Like a Pro | by myHotTake | Medium
[Link]

2 26 – React
[Link]

3 useMemo – React
[Link]

5 7 30 49 52 30 Basic to Advanced React Interview Questions with Solution | Blog


[Link]

6 Ref Forwarding with React forwardRef | Refine


[Link]

8 react hooks - is useMemo required to manage state via the context API in reactjs? - Stack Overflow
[Link]

9Performance Optimization Techniques with React’s useContext | by Love Trivedi | ZestGeek |


Medium
[Link]

10 11 12 51 Error Boundaries – React


[Link]

13 Code-Splitting – React
[Link]

14 15 16 17 18 createPortal – React
[Link]

19 20 useTransition – React
[Link]

21 22 useDeferredValue – React
[Link]

23 24 React Concurrency, Explained: What useTransition and Suspense Hydration Actually Do ·


53

PerfPerfPerf
[Link]

25 27 28 29 50 Optimizing Performance – React


[Link]

32 Complete Guide to React Router v6: A Step-by-Step Tutorial


[Link]

23
33 React Redux Saga example app.. Click Here to see a live example of… | by Ron Lavit | Medium
[Link]

34 35 36 37 Usage Guide | Redux Toolkit


[Link]

38 39 40 43 Data Fetching: getServerSideProps | [Link]


[Link]

41 42 44 54 Data Fetching: getStaticProps | [Link]


[Link]

45 Snapshot Testing · Jest


[Link]

46 47 48 Best Practices for Using React Testing Library | by Dzmitry Ihnatovich | Medium
[Link]

24

You might also like