React Basics
Click ★ if you like the project. Your contributions are heartily ♡ welcome.
Related Topics
- HTML Basics
- CSS Basics
- JavaScript Basics
- React Coding Practice
- React Multiple Choice Questions
- React Quick Reference
- React 19 Interview Questions
- Next.js Interview Questions
- Redux Interview Questions
- Redux Quick Reference
- Jest Quick Reference
- React Best Practices
- React Unit Testing
- React Unit Testing Project
- React Project - ToDo App
- React Project - My Store App
Table of Contents
- React
- Advanced Patterns
- Optimization & Internationalization
- Enterprise & Advanced Topics
- Technical Lead Role
# 1. INTRODUCTION
Q. What is React.js?
React is a JavaScript library created for building fast and interactive user interfaces for web and mobile applications. It is an open-source, component-based, front-end library responsible only for the application view layer.
The main objective of ReactJS is to develop User Interfaces (UI) that improves the speed of the apps. It uses virtual DOM (JavaScript object), which improves the performance of the app. The JavaScript virtual DOM is faster than the regular DOM. We can use ReactJS on the client and server-side as well as with other frameworks. It uses component and data patterns that improve readability and helps to maintain larger apps.
Reference:
Q. How React works?
-
Virtual DOM: React maintains a lightweight copy of the actual DOM in memory. When state changes, React creates a new virtual DOM tree, compares it with the previous one (diffing), and calculates the minimal set of changes needed to update the real DOM.
-
Component-Based Architecture: React apps are built from reusable components - JavaScript functions or classes that return JSX (HTML-like syntax). Each component manages its own state and props (data passed from parent).
-
Reconciliation: When data changes, React uses its reconciliation algorithm to efficiently update only the parts of the DOM that actually changed, rather than re-rendering everything.
-
Unidirectional Data Flow: Data flows one way - from parent to child components via props. Child components communicate back via callback functions, maintaining predictable data flow.
Q. List some of the major advantages and limitations of React?
Major Advantages:
- Virtual DOM - React uses a Virtual DOM to track UI changes and update only the necessary elements rather than reloading the entire page, significantly boosting application speed.
- Reusable Component Architecture: Applications are built using self-contained, modular components that can be reused across different parts of a project or even in separate applications, improving maintainability.
- SEO-Friendly - Server-side rendering with Next.js improves search engine visibility
- One-Way Data Flow - Predictable data management and easier debugging
- JSX Syntax - Combines HTML and JavaScript for better readability
- React Native - Code reuse for mobile app development
- Easy Learning Curve - Simple API and good documentation
Limitations:
- Learning curve. Being not full-featured framework it is required in-depth knowledge for integration user interface free library into MVC framework.
- View-orientedness is one of the cons of ReactJS. It should be found ‘Model’ and ‘Controller’ to resolve ‘View’ problem.
- Not using isomorphic approach to exploit application leads to search engines indexing problems.
Q. Why does React emphasize on unidirectional data flow?
React emphasizes unidirectional data flow (also called “one-way data binding”) because it provides a predictable, easy-to-debug, and modular way to manage how data moves through an application.
In this pattern, data is passed from parent to child components via read-only props. If a child needs to change the data, it doesn't modify it directly; instead, it triggers a callback function provided by the parent to update the “single source of truth”.
Key Benefits
- Predictability: Because data follows a strict one-way path, you always know where a specific piece of state originates and what caused it to change.
- Easier Debugging: Tracing bugs becomes a linear process: you check the output, the component, and then move up the tree until you find the source of the state.
- Efficiency: React can easily determine which parts of the UI need to re-render by simply checking if props or state have changed, reducing unnecessary updates.
- Decoupling: Child components focus purely on rendering (often called “dumb” or presentational components), while parent components handle the logic and state management.
- Scalability: This pattern scales well for large apps. Tools like the React Context API or Redux extend these same unidirectional principles to global state
Example:
// Parent controls state
function Parent() {
const [data, setData] = useState('hello');
return <Child data={data} onChange={setData} />;
}
// Child receives data and callback
function Child({ data, onChange }) {
return <input value={data} onChange={(e) => onChange(e.target.value)} />;
}
Q. What is Destructuring in React.js?
Destructuring in React.js is a JavaScript ES6 feature that allows you to extract values from arrays or properties from objects into distinct variables. In React, it is widely used to make code cleaner and more readable.
Key Uses in React:
1. Props Destructuring:
// Without destructuring
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// With destructuring
function Welcome({ name, age }) {
return <h1>Hello, {name}, you are {age} years old</h1>;
}
2. State Destructuring with Hooks:
// Array destructuring with useState
const [counter, setCounter] = React.useState(0);
const [name, setName] = React.useState("John");
// Object destructuring with useContext
const { user, theme } = useContext(AppContext);
3. Nested Object Destructuring:
const user = {
name: "Alice",
address: {
city: "New York",
country: "USA"
}
};
// Destructure nested properties
const { name, address: { city } } = user;
// name = "Alice", city = "New York"
4. Class Component State:
class App extends React.Component {
render() {
const { isLoggedIn, user } = this.state;
return <div>{isLoggedIn ? user.name : "Guest"}</div>;
}
}
Q. Why is it necessary to start component names with a capital letter?
In React, component names must start with a capital letter because of how JSX distinguishes between HTML elements and React components:
Key Reasons:
1. JSX Compilation Behavior:
<component />→ compiles to React.createElement(‘component’) (treated as HTML tag string)<Component />→ compiles to React.createElement(Component) (treated as React component)
2. Built-in vs Custom Components:
- Lowercase names refer to built-in HTML elements like
<div>,<span>,<button> - Capitalized names refer to custom React components you've defined or imported
3. Exception:
Lowercase with dot notation works: <obj.component /> compiles to React.createElement(obj.component)
Example:
// ❌ Wrong - React treats this as HTML <mybutton> tag
function myButton() {
return <button>Click me</button>;
}
// Using it: <myButton /> // Won\'t work as expected
// ✅ Correct - React treats this as a component
function MyButton() {
return <button>Click me</button>;
}
// Using it: <MyButton /> // Works correctly
Q. What are fragments?
React Fragments are a way to group multiple elements without adding an extra node to the browser's DOM.
Why use Fragments?
- Avoid extra DOM nodes that can affect styling and layout
- Cleaner DOM structure
- Better performance (fewer nodes to render)
- Satisfy React's requirement that components return a single element
- Adding a
<div>inside elements like<table>,<tr>, or<ul>can break the browser's layout rules.
Example:
// Long syntax
import React, { Fragment } from 'react';
function MyComponent() {
return (
<Fragment>
<h1>Title</h1>
<p>Description</p>
</Fragment>
);
}
// Short syntax (more common)
function MyComponent() {
return (
<>
<h1>Title</h1>
<p>Description</p>
</>
);
}
Q. What is Virtual DOM?
The Virtual DOM is a programming concept where a “virtual” representation of a User Interface is kept in memory and synchronized with the “real” browser DOM by a library like ReactDOM
How it works:
1. Initial Render: React creates a lightweight tree of JavaScript objects (the Virtual DOM) that mirrors the structure of the real DOM.
2. State Change: When a component's state or props change, React creates a new Virtual DOM tree.
3. Diffing: React compares the new tree with the previous version to identify exactly what changed. This is handled by a highly efficient diffing algorithm.
4. Reconciliation: Based on the “diffs,” React calculates the minimal set of changes needed and updates only those specific parts in the real DOM.
Benefits:
- Performance - Direct DOM manipulation is expensive. The Virtual DOM allows React to minimize actual DOM operations
- Efficiency - Instead of updating the entire DOM, only changed elements are updated
- Declarative - Developers describe what the UI should look like, and React handles the efficient updates
Example:
// When count changes from 0 to 1
const [count, setCount] = useState(0);
return <div>Count: {count}</div>;
React only updates the text node containing the count value, not the entire <div> element.
Q. What is the difference between ShadowDOM and VirtualDOM?
1. Document Object Model:
It a way of representing a structured document via objects. It is cross-platform and language-independent convention for representing and interacting with data in HTML, XML, and others. Web browsers handle the DOM implementation details, so we can interact with it using JavaScript and CSS.
2. Virtual DOM:
Virtual DOM is any kind of representation of a real DOM. Virtual DOM is about avoiding unnecessary changes to the DOM, which are expensive performance-wise, because changes to the DOM usually cause re-rendering of the page. It allows to collect several changes to be applied at once, so not every single change causes a re-render, but instead re-rendering only happens once after a set of changes was applied to the DOM.
3. Shadow DOM:
Shadow DOM is mostly about encapsulation of the implementation. A single custom element can implement more-or-less complex logic combined with more-or-less complex DOM. Shadow DOM refers to the ability of the browser to include a subtree of DOM elements into the rendering of a document, but not into the main document DOM tree.
Difference:
The virtual DOM creates an additional DOM. The shadow DOM simply hides implementation details and provides isolated scope for web components.
Virtual DOM is about performance (efficiently updating the UI), while Shadow DOM is about encapsulation (isolating component styles and structure from the rest of the page).
Note:
React uses Virtual DOM but doesn't use Shadow DOM by default.
Q. What is React Native and how does it differ from React (React DOM)?
React (or React DOM) is a JavaScript library for building web user interfaces that render to the browser's DOM.
React Native is a framework for building native mobile applications for iOS and Android using React concepts — but instead of rendering HTML to the browser, it renders native platform components (like UIView on iOS and View on Android).
How React Native works:
Your JavaScript/JSX code
↓
React Native Bridge (JS thread ↔ Native thread)
↓
Native Platform APIs → Real iOS/Android UI components
Syntax differences:
// React (Web) — renders HTML elements
import React from 'react';
function WebButton({ label, onClick }) {
return (
<div style=>
<button onClick={onClick}>{label}</button>
</div>
);
}
// React Native — renders native components (NOT HTML)
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
function NativeButton({ label, onPress }) {
return (
<View style={styles.container}>
<TouchableOpacity onPress={onPress}>
<Text>{label}</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: { padding: 10, backgroundColor: 'blue' },
});
Key differences:
| Feature | React (Web) | React Native |
|---|---|---|
| Renders to | Browser DOM (div, span, button) | Native platform views (View, Text, Image) |
| Styling | CSS (class names, CSS-in-JS, modules) | StyleSheet API (subset of CSS, no cascading) |
| Navigation | React Router, History API | React Navigation, Expo Router |
| Flexbox | Standard CSS flex | Flex with column direction default |
| Animations | CSS animations, Framer Motion | Animated API, Reanimated |
| Platform access | Web APIs (localStorage, fetch) | Device APIs (camera, GPS, notifications) |
| Build output | HTML/CSS/JS bundle | iOS .ipa / Android .apk |
| Dev tooling | Browser DevTools | Expo Go, Metro Bundler, Flipper |
What React and React Native share:
- Component model (functional components, hooks, props, state)
- Same core React APIs:
useState,useEffect,useContext,useRef, etc. - JSX syntax
- Component lifecycle behavior
- Context API, React.memo, lazy loading
Code sharing strategies:
monorepo/
├── packages/
│ ├── shared/ ← Shared business logic, hooks, utilities (no UI)
│ │ ├── useAuth.ts
│ │ └── formatDate.ts
│ ├── web/ ← React DOM components
│ └── mobile/ ← React Native components
Expo: The most common React Native development environment. Provides a managed workflow with pre-built native modules, OTA updates, and a simple build system.
npx create-expo-app my-app
Q. How does React compare to Vue and Angular?
React, Vue, and Angular are the three dominant front-end technologies, but they serve different purposes and follow different philosophies.
React (by Meta):
- A UI library, not a full framework — it handles the View layer only.
- Uses JSX (JavaScript + HTML together).
- Unidirectional data flow (parent → child via props).
- Relies on the ecosystem for routing (
react-router), state management (Redux,Zustand,Context), and form handling. - Highly flexible but requires assembling your own stack.
- Introduced Hooks (v16.8) to enable functional-component patterns.
Vue (by Evan You):
- A progressive framework — can be adopted incrementally.
- Uses single-file components (
.vuefiles): HTML template, script, and style in one file. - Supports both one-way and two-way data binding (
v-model). - Built-in routing (
vue-router) and state (Vuex/Pinia) are official packages. - Gentler learning curve than React or Angular.
Angular (by Google):
- A full-featured, opinionated framework — includes everything out of the box.
- Uses TypeScript by default.
- Two-way data binding via
ngModel. - Built-in dependency injection, routing, forms (
ReactiveFormsModule), HTTP client, and testing. - Steeper learning curve but fewer architectural decisions to make.
Comparison Table:
| Feature | React | Vue | Angular |
|---|---|---|---|
| Type | UI Library | Progressive Framework | Full Framework |
| Language | JSX | HTML Templates | TypeScript |
| Data Binding | One-way | One-way + v-model | Two-way (ngModel) |
| State Mgmt | External (Redux, Zustand) | Pinia / Vuex | NgRx / Services |
| Learning Curve | Medium | Low | High |
| Backed By | Meta | Community | |
| Mobile | React Native | NativeScript/Ionic | Ionic |
When to choose:
- React — large teams, flexible architecture, strong ecosystem, React Native for mobile.
- Vue — rapid prototyping, smaller teams, gentle learning curve.
- Angular — enterprise applications, large teams that want an opinionated, batteries-included solution.
# 2. REACT SETUP
Q. How to set up a react project with Vite?
Vite is a modern, fast build tool that provides a superior development experience compared to traditional bundlers. It offers instant server start, lightning-fast Hot Module Replacement (HMR), and optimized builds.
It replaces older tools like Create React App by providing a leaner, faster build process
Requirements:
Installation:
npm create vite@latest my-react-app -- --template react #Initialize the Project
cd my-react-app
npm install #Install Dependencies
npm run dev #Start the Development Server
Essential Vite Commands:
npm run dev #Starts the development server with Hot Module Replacement (HMR).
npm run build #Creates an optimized production bundle in the /dist folder.
npm run preview #Locally previews the production build you just created.
Available Templates:
react- React with JavaScriptreact-ts- React with TypeScriptreact-swc- React with SWC (faster compilation)react-swc-ts- React with TypeScript and SWC
Project Structure:
my-react-app
├── node_modules
├── public
│ └── vite.svg
├── src
│ ├── assets
│ │ └── react.svg
│ ├── App.css
│ ├── App.jsx
│ ├── index.css
│ └── main.jsx
├── .gitignore
├── index.html
├── package.json
├── vite.config.js
└── README.md
Key Differences from Create React App:
- Faster startup - Vite uses native ES modules and doesn't bundle during development
- Instant HMR - Updates reflect immediately without full page reload
- Optimized builds - Uses Rollup for production builds
- Smaller bundle sizes - Better tree-shaking and code splitting
- Modern by default - No legacy browser support overhead
Reference:
Q. What are the features of Vite?
Vite is a modern build tool and development server with these key features:
Core Features
- Lightning-fast cold start using native ES modules
- Instant Hot Module Replacement (HMR) that stays fast regardless of app size
- No bundling during development
Optimized Production Build
- Uses Rollup for production bundling
- Pre-configured and optimized out of the box
- Code splitting and tree-shaking
Rich Features
- TypeScript support out of the box
- JSX/TSX support
- CSS pre-processors (Sass, Less, Stylus)
- PostCSS support
- Static asset handling
- JSON importing
Q. What are the popular React-specific linter?
1. ESLint:
ESLint is a popular JavaScript linter. There are plugins available that analyse specific code styles. One of the most common for React is an npm package called eslint-plugin-react.
npm install -g eslint-plugin-react
This will install the plugin we need, and in our ESLint config file, we just need a few extra lines.
"extends": [
"eslint:recommended",
"plugin:react/recommended"
]
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"lint": "eslint src/**/*.js src/**/*.jsx"
}
2. eslint-plugin-jsx-a11y:
- Focuses on accessibility issues in JSX
- Catches issues with
alttext,tabindex, and other accessibility concerns - Works alongside ESLint to ensure React components are accessible
3. Other Popular Options:
- eslint-plugin-react-hooks - Enforces Rules of Hooks
- @typescript-eslint/eslint-plugin - For TypeScript React projects
- Prettier - Code formatter that works well with ESLint
- eslint-config-airbnb - Airbnb's ESLint config includes React rules
Q. What is the browser support for react applications?
React applications are supported by all modern web browsers. Because React uses JavaScript ES5 features as its baseline, it can be extended to older browsers with the right configuration.
Support for Internet Explorer 9, 10, and 11 requires polyfills. For a set of polyfills to support older browsers, use react-app-polyfill.
Browser Configuration:
The browserslist configuration in package.json controls which browsers are supported by determining what JavaScript transformations are applied:
Example:
// package.json
"browserslist": {
"production": [
">0.2%", // Browsers with >0.2% market share
"not dead", // Still maintained browsers
"not op_mini all" // Exclude Opera Mini
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
Q. Explain the use of Webpack and Babel in React?
1. Webpack (The Transpiler):
Webpack primary job is to take all your individual files (JavaScript, CSS, images) and “bundle” them into a few optimized files.
-
Dependency Management: It starts from an entry point (usually index.js) and creates a dependency graph to understand how every file is connected.
-
Asset Handling: It treats everything—including CSS and images—as a module, allowing you to import them directly into your JavaScript components.
-
Performance: It performs optimizations like tree shaking (removing unused code), minification, and code splitting (loading only what is needed).
-
Development Features: Tools like webpack-dev-server enable Hot Module Replacement (HMR), allowing you to see changes instantly without a full page refresh.
2. Babel (The Module Bundler):
Babel is a “translator” that converts modern code into a version that all browsers can understand.
-
JSX Transformation: Browsers cannot natively read JSX (the HTML-like syntax in React). Babel converts JSX into standard React.createElement() calls.
-
Modern JS (ES6+) Support: It transpiles newer features (like arrow functions, classes, and destructuring) into ES5 syntax for compatibility with older browsers.
-
Future-Proofing: It allows developers to use experimental JavaScript features before they are officially supported by browsers
How they work together:
- You write React code with JSX and modern JavaScript
- Webpack bundles your modules and passes JavaScript files through babel-loader
- Babel transpiles JSX → React.createElement() calls and ES6+ → ES5
- Webpack outputs optimized bundles for the browser
Q. What is ReactDOM?
ReactDOM is a package that provides DOM-specific methods for managing DOM elements in web applications. It serves as the bridge between React components and the browser's DOM.
ReactDOM provides the developers with an API containing the following methods
Key Methods (React 18+):
-
createRoot() - Creates a React root for displaying content inside a browser DOM element. Use it to render and update your React component tree.
-
hydrateRoot() - Replaces
hydrate()for server-side rendering. Attaches React to existing server-rendered HTML and enables React to manage it. -
root.unmount() - Destroys a rendered React tree inside a root, cleaning up all associated resources.
-
createPortal() - The
createPortal()allows rendering a component into a DOM node that resides outside the current DOM hierarchy of the parent component.
Note:
ReactDOM.render(), ReactDOM.hydrate(), ReactDOM.unmountComponentAtNode(), and ReactDOM.findDOMNode() are deprecated as of React 18. Use the react-dom/client APIs instead.
Syntax:
import { createRoot, hydrateRoot } from 'react-dom/client';
import { createPortal } from 'react-dom';
const root = createRoot(container); // createRoot()
root.render(element); // render()
root.unmount(); // unmount()
hydrateRoot(container, element); // hydrateRoot() — replaces hydrate()
createPortal(child, container); // createPortal()
Example:
import { createRoot } from 'react-dom/client';
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
root.render(<App />);
Q. What is the difference between ReactDOM and React?
React is the core library that contains the fundamental React functionality used across all platforms (web, mobile, etc.)
ReactDOM is a separate package that provides DOM-specific methods for web applications - it's the bridge between React components and the browser's DOM.
Key Differences:
| React | ReactDOM |
|---|---|
| It is core engine. It manages component logic, state, hooks, and props. | The web renderer. It translates React components into actual HTML DOM nodes. |
| It is used for Creating the Virtual DOM (a blueprint of what the UI should look like). | Updating the Real DOM (the actual elements visible in the browser). |
| It is Platform-independent; used for Web, Mobile, VR, etc. | Browser-specific; used only for web applications. |
| useState, useEffect, createElement, Component. | createRoot, createPortal, hydrateRoot. |
Example:
/**
* React vs ReactDOM
*/
import React from 'react'; // Core functionality
import { createRoot } from 'react-dom/client'; // DOM rendering
export default function App() {
return <h1>Hello React</h1>;
}
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(<App />);
⚝ Try this example on CodeSandbox
Q. How to build a progressive web app with react?
A Progressive Web App (PWA) is a web application that uses modern web capabilities to deliver an app-like experience to users. React applications can be converted into PWAs by adding service workers, manifest files, and implementing offline capabilities.
Building a Progressive Web App (PWA) with React is mostly about adding a web app manifest, a service worker (for Offline, Caching, and updates), and meeting installability criteria (HTTPS, icons, etc).
Steps to Build a PWA with React:
1. Vite with PWA Template:
Vite offers a modern approach for building PWAs using the vite-plugin-pwa package.
# Create new React app with PWA template
npm create vite@latest my-pwa-app -- --template react
cd my-pwa-app
npm install
npm install vite-plugin-pwa -D # Install the PWA package
2. Registering the Service Worker:
The Service Worker manage tasks between browser and server, intercepting network requests, managing caching, and providing offline functionality.
Basic Service Worker Registration:
Add this code to your src/main.jsx file:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
// Service Worker registration
if ('serviceWorker' in navigator) {
// Check if browser supports Service Worker
window.addEventListener('load', () => {
// Execute after page is fully loaded
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('SW registered: ', registration);
// Registration successful
})
.catch(registrationError => {
console.log('SW registration failed: ', registrationError);
// Registration failed
});
});
}
3. Configuring the Manifest File:
Modify your vite.config.js to include and configure the plugin, specifying manifest and service worker options.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
react(),
VitePWA({
registerType: 'autoUpdate', // Automatically update Service Worker
includeAssets: ['favicon.ico', 'robots.txt', 'apple-touch-icon.png'], // Files to cache
manifest: {
name: 'My First PWA', // Full app name
short_name: 'PWA App', // Short name displayed on home screen
description: 'My first Progressive Web App built with React',
theme_color: '#ffffff', // Color of the top bar
background_color: '#ffffff', // Splash screen background color
display: 'standalone', // Makes it look like a native app (hides browser UI)
icons: [
{
src: 'pwa-192x192.png', // Small icon
sizes: '192x192',
type: 'image/png'
},
{
src: 'pwa-512x512.png', // Large icon
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable' // Works in various environments
}
]
}
})
]
})
Key PWA Features:
- Offline Support - Works without internet connection
- Installable - Can be installed on home screen
- Fast - Instant reloads via caching
- Responsive - Works on any device size
- Secure - Served over HTTPS
Reference:
Q. What is the difference between vite and webpack?
The primary difference between Vite and Webpack is how they handle code during development. While Webpack bundles your entire application before serving it, Vite serves individual files directly to the browser using modern web standards.
Key Differences:
Webpack:
Webpack is a module bundler that:
- Bundles JavaScript files and dependencies
- Transforms and optimizes assets (CSS, images, fonts)
- Requires manual configuration
- Provides full control over build process
- Can be used with any JavaScript framework
Vite:
- Uses native ES modules during development (no bundling)
- Instant cold start (serves files on-demand)
- Lightning-fast Hot Module Replacement (HMR)
- Pre-configured and optimized out-of-the-box
- Uses Rollup for production builds
Example: Webpack Configuration (Manual Setup)
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
})
],
devServer: {
port: 3000,
hot: true
}
};
Example: Vite (Zero Configuration)
# Just one command!
npx create-react-app my-app
cd my-app
npm start
When to Use Webpack:
- Working with legacy codebases
- Need specific advanced configurations
- Require specific plugins not available in Vite
- Complex multi-page applications with custom requirements
When to Use Vite:
- Starting new React projects
- Want faster development experience
- Prefer minimal configuration
- Building modern applications
# 3. REACT JSX
Q. What is JSX?
JSX (JavaScript XML) is a syntax extension that allows you to write HTML-like code directly in JavaScript. It is used in React to describe what the UI should look like.
JSX is not required for React, but it makes code more readable and easier to write. Under the hood, it's just syntactic sugar for React.createElement() calls.
Key Points:
- No DOM methods needed - Write HTML in JavaScript without
createElement()orappendChild() - Compiles to JavaScript - JSX transforms into regular JavaScript objects
- Type-safe - Most errors caught during compilation
- Performance - Optimizations applied during compilation
Example:
// JSX syntax
const hello = <h1 className="greet">Hello World</h1>
// Compiles to:
const hello = React.createElement("h1", {
className: "greet"
}, "Hello World")
In a component:
export default function App() {
return (
<div className="App">
<h1>Hello World!</h1>
</div>
);
}
⚝ Try this example on CodeSandbox
Q. How JSX prevents Injection Attacks?
JSX prevents injection attacks, specifically Cross-Site Scripting (XSS), by automatically escaping any values embedded within JSX elements before they are rendered to the DOM. This means all dynamic content is converted into plain strings, neutralizing potentially malicious code.
Example:
export default class JSXInjectionExample extends React.Component {
constructor(props) {
super(props);
this.state = {
userContent: `JSX prevents Injection Attacks Example
<script src="http://example.com/malicious-script.js></script>`
};
}
render() {
return (
<div>User content: {this.state.userContent}</div>
);
}
}
// Output
User content: JSX prevents Injection Attacks Example
<script src="http://example.com/malicious-script.js></script>
⚝ Try this example on CodeSandbox
Q. What are the benefits of new JSX transform?
The new JSX transform, React 19, provides several key benefits for developers and application performance
-
No Explicit React Import: You can use JSX syntax without having to import React from ‘react’ at the top of every file. The compiler now automatically imports special functions from new entry points (react/jsx-runtime) to handle the transformation.
-
Reduced Bundle Size: The new transform generates more optimized code compared to the old
React.createElement()method, which can lead to a slight reduction in the overall application bundle size. -
Performance Optimizations: It eliminates some of the overhead associated with the previous transformation process, enabling faster runtime performance and more efficient rendering.
-
Simplified Configuration: Modern versions of React include this transform by default, reducing the need for manual Babel or TypeScript configuration with multiple presets and plugins.
-
Enhanced Developer Experience: The streamlined process can result in cleaner stack traces and more useful error messages, making debugging more straightforward.
Q. Is it possible to use React without rendering HTML?
It is possible with latest version (>=16.2). Below are the possible options:
// 1. Return false
render() {
return false
}
// 2. Return null (most common)
render() {
return null
}
// 3. Return empty array
render() {
return []
}
// 4. Return empty Fragment (long syntax)
render() {
return <React.Fragment></React.Fragment>
}
// 5. Return empty Fragment (short syntax)
render() {
return <></>
}
Note:
React can also run on the server side so, it will be possible to use it in such a way that it doesn't involve any DOM modifications (but maybe only the virtual DOM computation).
Q. How to write comments in React and JSX?
Writing comments in React components can be done just like comment in regular JavaScript classes and functions.
React comments:
function App() {
// Single line Comment
/*
* multi
* line
* comment
**/
return (
<h1>My Application</h1>
);
}
JSX comments:
export default function App() {
return (
<div>
{/* A JSX comment */}
<h1>My Application</h1>
</div>
);
}
Q. How to add custom DOM attributes in JSX?
Custom attributes are supported natively in React 16+. This means that adding a custom attribute to an element is now as simple as adding it to a render function, like so:
Syntax:
// 1. Custom DOM Attribute
render() {
return (
<div custom-attribute="some-value" />
);
}
// 2. Data Attribute ( starts with "data-" )
render() {
return (
<div data-id="10" data-user-role="admin"/>
);
}
// 3. ARIA Attribute ( starts with "aria-" )
render() {
return (
<button aria-label="Close" onClick={onClose} />
);
}
Example:
function CustomAttributeExample() {
return (
<div
custom-attribute="custom-value"
data-testid="my-component"
data-category="widgets"
aria-label="Product information"
>
Content with custom attributes
</div>
);
}
⚝ Try this example on CodeSandbox
Q. How many outermost elements can be there in a JSX expression?
A JSX expression must have only one outer element. For Example:
const headings = (
<div id = "outermost-element">
<h1>I am a heading </h1>
<h2>I am also a heading</h2>
</div>
)
Q. How to loop inside JSX?
To loop inside JSX, use the Array.prototype.map() method with ES6 arrow functions. This is the standard React pattern for rendering lists of elements.
Example:
/**
* Loop inside JSX
*/
const items = ['Apple', 'Banana', 'Cherry'];
function FruitList() {
return (
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
}
⚝ Try this example on CodeSandbox
Q. How do you print false values in JSX?
In React, boolean values (true and false), null, and undefined are valid children, but these values will not be rendered in UI if you put them directly inside {} in JSX.
For example, all these JSX expressions will result in the same empty div:
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>
If you want a value like false, true, null, or undefined to show in the output, you have to convert it to a string first.
<div>{String(true)}</div>
<div>{String(false)}</div>
<div>{String(undefined)}</div>
<div>{String(null)}</div>
In the output, this will render true, false, undefined, and null respectively.
⚝ Try this example on CodeSandbox
Q. How to use React label element?
In React, you cannot use the standard HTML for attribute with <label> elements because for is a reserved keyword in JavaScript. Instead, React uses htmlFor.
❌ Wrong - Using for (produces warning):
<label for={'user'}>{'User'}</label>
<input type={'text'} id={'user'} />
✅ Correct - Using htmlFor:
<label htmlFor={'user'}>{'User'}</label>
<input type={'text'} id={'user'} />
Example:
function LoginForm() {
return (
<div>
<label htmlFor="username">Username:</label>
<input type="text" id="username" name="username" />
<label htmlFor="password">Password:</label>
<input type="password" id="password" name="password" />
</div>
);
}
Q. How to use InnerHtml in React?
The innerHTML is risky because it is easy to expose users to a cross-site scripting (XSS) attack. React provides dangerouslySetInnerHTML as a replacement for innerHTML. It allows to set HTML directly from React by using dangerouslySetInnerHTML and passing an object with a __html key that holds HTML.
Example:
/**
* InnerHtml in React
*/
export default function App() {
return (
<div
dangerouslySetInnerHTML=
></div>
);
}
Example: Using sanitization
import DOMPurify from 'dompurify';
function SafeHTML({ html }) {
const sanitizedHTML = DOMPurify.sanitize(html);
return (
<div dangerouslySetInnerHTML= />
);
}
⚝ Try this example on CodeSandbox
Q. How to show and hide elements in React?
1. Returning Null:
const AddToCart = ({ available }) => {
if (!available) return null
return (
<div className="full tr">
<button className="product--cart-button">Add to Cart</button>
</div>
)
}
2. Ternary Display:
When you need to control whether one element vs. another is displayed, or even one element vs. nothing at all (null), you can use the ternary operator embedded inside of a larger portion of JSX.
<div className="half">
<p>{description}</p>
{remaining === 0 ? (
<span className="product-sold-out">Sold Out</span>
) : (
<span className="product-remaining">{remaining} remaining</span>
)}
</div>
In this case, if there are no products remaining, we will display “Sold Out”; otherwise we will display the number of products remaining.
3. Shortcut Display:
It involves using a conditional inside of your JSX that looks like checkIfTrue && <span>display if true</span>. Because if statements that use && operands stop as soon as they find the first value that evaluates to false, it won't reach the right side (the JSX) if the left side of the equation evaluates to false.
<h2>
<span className="product--title__large">{nameFirst}</span>
{nameRest.length > 0 && (
<span className="product--title__small">{nameRest.join(" ")}</span>
)}
</h2>
4. Using Style Property:
<div style=>info</div>
Q. How does Babel transpile JSX?
Babel is a JavaScript compiler that converts modern JavaScript (including JSX) into backwards-compatible JavaScript that browsers can understand. JSX is not valid JavaScript on its own — Babel transforms it into React.createElement() calls at build time.
Step-by-step transpilation:
1. Old JSX Transform (React 16 and earlier):
// What you write
const element = <h1 className="title">Hello, World!</h1>;
// What Babel compiles it to
const element = React.createElement(
"h1",
{ className: "title" },
"Hello, World!"
);
This required import React from 'react' in every file.
2. New JSX Transform (React 17+):
// What you write
const element = <h1 className="title">Hello, World!</h1>;
// What Babel compiles it to (automatic runtime)
import { jsx as _jsx } from "react/jsx-runtime";
const element = _jsx("h1", { className: "title", children: "Hello, World!" });
The new transform automatically imports the jsx runtime, so you no longer need to import React at the top of every file.
How it works in the build pipeline:
Your JSX code
↓
Babel (with @babel/preset-react)
↓
React.createElement() / jsx() calls (plain JavaScript)
↓
Webpack/Vite bundles the output
↓
Browser executes standard JavaScript
Babel Configuration:
// babel.config.json
{
"presets": [
["@babel/preset-react", { "runtime": "automatic" }]
]
}
Key Points:
- JSX is syntactic sugar — it always compiles down to function calls.
- The
runtime: "automatic"option enables the new JSX transform (no manual React import). - Babel also transpiles ES6+ features (arrow functions, destructuring, async/await) for older browser compatibility.
- Vite uses
@vitejs/plugin-react(which uses Babel under the hood) to handle JSX transformation.
# 4. REACT COMPONENTS
Q. What are React components?
React components are the essential building blocks of a React application. They are independent, reusable pieces of code that serve the same purpose as JavaScript functions but work in isolation to return UI elements.
Two Main Types:
- Stateful Components - Hold and manage their own state
- Stateless Components - Have no state, only receive data via props
1. Stateless Component:
const Welcome = (props) => {
return <h1>Hello, {props.name}</h1>;
};
const App = () => {
const name = 'React'
return (
<div>
<Welcome name={name} />
</div>
);
};
export default App;
The above example shows a stateless component named Welcome which is inserted in the <App/> component. The Welcome just comprises of a <h1> element. Although the Stateless component has no state, it still receives data via props from a parent component.
2. Stateful Component:
import React, { useState } from 'react'
const ExampleComponent = (props) => {
const [email, setEmail] = useState(props.defaultEmail) // state
const changeEmailHandler = (e) => {
setEmail(e.target.value)
}
return (
<input type="text" value={email} onChange={changeEmailHandler} />
);
}
const App = () => {
const defaultEmail = "suniti.mukhopadhyay@gmail.com"
return (
<div>
<ExampleComponent defaultEmail={defaultEmail} />
</div>
);
};
export default App;
The above example shows a stateful component named ExampleComponent which is inserted in the <App/> component. The ExampleComponent contains a <input>. First of all, in the ExampleComponent, we need to assign defaultEmail by props to a local state by a useState() hook in ExampleComponent.
Next, we have to pass email to value property of a input tag and pass a function changeEmailHandler to an onChange() event for a purpose keeping track of the current value of the input.
Q. What is the difference between Component and Container in React?
In React, the “Component” (specifically a “Presentational” component) vs. “Container” distinction refers to a popular design pattern used to separate logic from presentation. While every “Container” is technically a React component, they serve different architectural roles.
Presentational Components:
- Concerned with how things look
- Receive data via props
- Usually stateless (just display UI)
- No data fetching or state management
Container Components:
- Concerned with how things work
- Manage and store their own state
- Handle data fetching and business logic
- Pass data down to presentational components
Example: Presentational Component
/**
* Presentational Component
*/
const Users = props => (
<ul>
{props.users.map(user => (
<li>{user}</li>
))}
</ul>
)
Example: Container Component
/**
* Container Component
*/
class UsersContainer extends React.Component {
constructor() {
this.state = {
users: []
}
}
componentDidMount() {
axios.get('/users').then(users =>
this.setState({ users: users }))
)
}
render() {
return <Users users={this.state.users} />
}
}
Q. How to import and export components using React.js?
React components are typically separated into individual files to keep code modular and manageable. React uses standard JavaScript ES6 module syntax for this purpose, primarily utilizing Default and Named exports
1. Default Export and Import (Recommended):
Use a default export when a file exports only one primary component.
- Export: You use the export default keywords before the component.
- Import: You import it without curly braces and can give it any name you prefer
Example:
// Button.js
export default function Button() {
return <button>Click me</button>;
}
// Or with class components
class Button extends Component {
render() {
return <button>Click me</button>;
}
}
export default Button;
// App.js
import MyButton from './Button'; // No curly braces; can rename 'Button' to 'MyButton'
2. Named Export and Import
Use named exports when you want to export multiple components or values from a single file.
- Export: Use the export keyword before the component declaration.
- Import: You must wrap the component names in curly braces {} and use their exact exported names
Example:
// Components.js
export function Header() { return <h1>Header</h1>; }
export function Footer() { return <footer>Footer</footer>; }
// App.js
import { Header, Footer } from './Components'; // Must use exact names
Key Differences
| Default Export | Named Export |
|---|---|
| One per file | Multiple per file |
| Import with any name | Import with exact name (or rename with as) |
export default Component |
export { Component } |
import Component from './file' |
import { Component } from './file' |
Q. What is difference between declarative and imperative in React.js?
The main difference is that imperative programming focuses on how to perform a task using explicit, step-by-step instructions, while declarative programming (which React uses) focuses on what the desired outcome should be, and lets the system figure out the how.
1. Declarative programming:
In React, you simply describe what the UI should look like for a given state, and React handles updating the DOM efficiently. You don't manually manipulate DOM elements—you declare the desired outcome and React takes care of the “how.”
This makes React code more:
- Readable - Easy to understand what the UI will look like
- Maintainable - Less boilerplate code
- Predictable - State changes automatically update the UI
Example:
/**
* React is Declarative
*/
const Welcome = () => (
<div id="App">
<h1>Hello World</h1>
</div>
)
render(<Welcome />, document.getElementById('root'))
2. Imperative programming:
Imperative programming requires you to write a sequence of commands that explicitly change the program's state step-by-step. This is common in vanilla JavaScript when directly manipulating the DOM.
/**
* vanilla JavaScript
*/
const string = "Hi there, I'm a web developer";
let removeSpace = "";
for (let i = 0; i < i.string.length; i++) {
if (string[i] === " ") removeSpace += "-";
else removeSpace += string[i];
}
console.log(removeSpace);
This example demonstrates imperative programming by looping through each character and replacing spaces one by one. The code requires extensive comments to convey its intent—without them, the logic is difficult to understand.
In contrast, declarative programming uses self-documenting syntax that clearly expresses the desired outcome while abstracting away the implementation details.
Q. What is the difference between Element and Component?
1. React Element:
It is a simple object that describes a DOM node and its attributes or properties. It is an immutable description object and you can not apply any methods on it.
Example:
const element = <h1>React Element Example!</h1>;
ReactDOM.render(element, document.getElementById('app'));
2. React Component:
A React Component is a function or class that accepts inputs (props) and returns a React element. It keeps references to DOM nodes and child component instances.
Example:
function Message() {
return <h2>React Component Example!</h2>;
}
ReactDOM.render(<Message />, document.getElementById('app'));
Key Differences:
| React Element | React Component | |
|---|---|---|
| A plain JavaScript object. | A function or a class. | |
| Describes what to see on screen. | Defines how to build the UI and logic. | |
| Immutable (cannot be changed). | Dynamic (can change via state/props). | |
| No Input (it is the data). | Accepts “Props” as arguments. | |
| No lifecycle | Has hooks (useEffect) and lifecycle. |
Q. How to conditionally render components in react?
Conditional rendering is a term to describe the ability to render different user interface (UI) markup if a condition is true or false. In React, it allows us to render different elements or components based on a condition.
1. if/else Statements (and Element Variables):
You can use variables to store elements. This can help you conditionally render a part of the component while the rest of the output doesn't change.
Example:
import { createRoot } from 'react-dom/client';
function LogInComponent({ isLoggedIn }) {
if (isLoggedIn) {
return <UserComponent />;
}
return <GuestComponent />;
}
const root = createRoot(document.getElementById('root'));
root.render(<LogInComponent isLoggedIn={false} />);
2. Ternary Operator(? :)
The ternary operator is a concise way to handle an if/else condition inline within JSX. It is ideal for simple, two-option conditions.
Example:
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}
3. Logical AND Operator (&&)
The logical && operator is useful when you want to render something only if a condition is true, and render nothing otherwise.
Example:
function Messages({ messageCount }) {
return (
<div>
{messageCount > 0 && <h2>You have {messageCount} new messages.</h2>}
</div>
);
}
4. switch Statements
For scenarios with multiple conditions, a switch statement can be cleaner and more readable than a long chain of if/else if statements.
Example:
function StatusMessage({ status }) {
const renderContent = () => {
switch (status) {
case 'loading':
return <p>Loading...</p>;
case 'success':
return <p>Data loaded successfully!</p>;
case 'error':
return <p>Error fetching data.</p>;
default:
return null;
}
};
return <div>{renderContent()}</div>;
}
5. Returning null to Prevent Rendering
To hide a component, you can have it return null. This prevents the component from rendering any output, although its lifecycle methods (like componentDidUpdate) still fire.
Example:
function WarningBanner({ showWarning }) {
if (!showWarning) {
return null;
}
return <div>Warning!</div>;
}
⚝ Try this example on CodeSandbox
Q. How to conditionally add attributes to React components?
In React, you can conditionally add attributes using several JavaScript patterns within your JSX. The most common methods include
1. Ternary Operator
Use this for toggling between two values or between a value and undefined. React will completely omit an attribute if its value is undefined, null, or false.
<button disabled={isLoading ? true : false}>
Submit
</button>
// Simplified for boolean attributes
<button disabled={isLoading}>Submit</button>
2. Logical AND Operator
<input
type="text"
{...(isRequired && { required: true })}
{...(maxLength && { maxLength: maxLength })}
/>
3. for Custom Data Attributes
function DataComponent({ mood, role }) {
return (
<div
data-mood={mood === 'happy' ? 'positive' : 'neutral'}
data-role={role || undefined}
aria-disabled={mood === 'sad'}
>
Content
</div>
);
}
⚝ Try this example on CodeSandbox
Q. How would you prevent a component from rendering?
There are several ways to prevent a component from rendering in React:
1. Return null
A component can return null from its render function to signify it should render nothing.
function MyComponent({ isVisible }) {
if (!isVisible) {
return null;
}
return <div>Content</div>;
}
2. Conditional Rendering:
function Parent() {
return (
<div>
{condition && <MyComponent />} {/* Short-Circuit Evaluation */}
{condition ? <MyComponent /> : null} {/* Ternary Operator */}
</div>
);
}
3. React.memo: (Functional Components)
Wraps your component to memoize it. It will only re-render if its props actually change.
const MyComponent = React.memo(({ value }) => {
return <div>{value}</div>;
});
4. shouldComponentUpdate() (Class Components)
Control when component updates:
class MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return nextProps.value !== this.props.value;
}
render() {
return <div>{this.props.value}</div>;
}
}
5. useMemo() Hook
Memoize the entire component output:
function Parent({ data }) {
const memoizedComponent = useMemo(() => {
return <ExpensiveComponent data={data} />;
}, [data]);
return memoizedComponent;
}
⚝ Try this example on CodeSandbox
Q. When would you use StrictMode component in React?
StrictMode is a development tool that helps identify bugs and outdated code during the development phase. It is a wrapper tool that enables additional runtime checks and warnings for its child components without affecting the production build.
Example:
/**
* StrictMode
*/
import { StrictMode } from "react";
import MyComponent from "./MyComponent";
export default function App() {
return (
<StrictMode>
<MyComponent />
</StrictMode>
);
}
React StrictMode, in order to be efficient and avoid potential problems by any side-effects, needs to trigger some methods and lifecycle hooks twice. These are:
- Class component constructor() method
- The render() method
- setState() updater functions (the first argument)
- The static getDerivedStateFromProps() lifecycle
- React.useState() function
Benefits of StrictMode:
- Identifying components with unsafe lifecycles
- Warning about legacy string ref API usage
- Warning about deprecated findDOMNode usage
- Detecting unexpected side effects
- Detecting legacy context API
Q. Why to avoid using setState() after a component has been unmounted?
In React, calling setState() on an unmounted component should be avoided primarily because it signals a potential memory leak in your application.
The component retains references in memory even after unmounting, preventing garbage collection. Subscriptions, timers, or async operations holding references to the component keep it alive unnecessarily
Warning Messages:
Warning: Can not perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
Prevention:
Use cleanup in useEffect():
useEffect(() => {
let isMounted = true;
fetchData().then(data => {
if (isMounted) {
setState(data);
}
});
return () => {
isMounted = false; // Cleanup
};
}, []);
The warning indicates a logic flaw where side effects aren't properly cleaned up, which can lead to performance issues and unexpected behavior in larger applications.
⚝ Try this example on CodeSandbox
Q. What is Lifting State Up in ReactJS?
In React, “Lifting State Up” is a pattern where you move state from child components to their closest common parent component so that multiple children can share and sync the same data.
Instead of each component managing its own copy of the data, the parent component becomes the “single source of truth”
Example:
The code shows an App component that manages player selection state and passes it down to:
PlayerContent(displays player buttons)PlayerDetails(displays selected player info)
When a button is clicked in PlayerContent, it calls updateSelectedPlayer() in the parent App, which updates state that both components depend on. This keeps both components synchronized with the same player selection.
/**
* Lifting State Up
*/
import PlayerContent from "./PlayerContent";
import PlayerDetails from "./PlayerDetails";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { selectedPlayer: [0, 0], playerName: "" };
this.updateSelectedPlayer = this.updateSelectedPlayer.bind(this);
}
updateSelectedPlayer(id, name) {
const arr = [0, 0, 0, 0];
arr[id] = 1;
this.setState({
playerName: name,
selectedPlayer: arr
});
}
render() {
return (
<div>
<PlayerContent
active={this.state.selectedPlayer[0]}
clickHandler={this.updateSelectedPlayer}
id={0}
name="Player 1"
/>
<PlayerContent
active={this.state.selectedPlayer[1]}
clickHandler={this.updateSelectedPlayer}
id={1}
name="Player 2"
/>
<PlayerDetails name={this.state.playerName} />
</div>
);
}
}
/**
* PlayerContent
*/
import React, { Component } from "react";
export default class PlayerContent extends Component {
render() {
return (
<button
onClick={() => {
this.props.clickHandler(this.props.id, this.props.name);
}}
style=
>
{this.props.name}
</button>
);
}
}
/**
* PlayerDetails
*/
import React, { Component } from "react";
export default class PlayerDetails extends Component {
render() {
return <h2>{this.props.name}</h2>;
}
}
⚝ Try this example on CodeSandbox
Q. What is “Children” in React?
In React, children is a special prop that allows you to pass components, elements, or any content between the opening and closing tags of a component.
Example:
// Component that uses children
const Picture = (props) => {
return (
<div>
<img src={props.imageUrl} />
{props.children} {/* Displays whatever is passed between tags */}
</div>
)
}
// Using the component with children
render() {
return (
<div className='container'>
<Picture imageUrl="photo.jpg">
<h2>Image Title</h2>
<p>Image description here</p>
</Picture>
</div>
)
}
In this example, the <h2> and <p> elements are the “children” of the <Picture> component. They're passed down and rendered wherever {props.children} appears in the Picture component.
Q. What is Compound Components in React?
Compound Components is an advanced React design pattern where multiple components work together to form a cohesive unit while sharing an implicit state.
How It Works:
Compound components use React's lower-level APIs:
React.Children.map()- to iterate over childrenReact.cloneElement()- to pass implicit props/state to childrenContext API(modern approach) - to share state
Example:
function App() {
return (
<Menu>
<MenuButton>Actions</MenuButton>
<MenuList>
<MenuItem>Download</MenuItem>
<MenuItem>Create</MenuItem>
<MenuItem>Delete</MenuItem>
</MenuList>
</Menu>
)
}
In this example, the <Menu> establishes some shared implicit state. The <MenuButton>, <MenuList>, and <MenuItem> components each access and/or manipulate that state, and it's all done implicitly. This allows you to have the expressive API you’re looking for.
# 4.1. FUNCTIONAL COMPONENTS
Q. What are functional components in react?
A React functional component is a simple JavaScript function that accepts props and returns a React element. It also referred as stateless components as it simply accept data and display them in some form.
After the introduction of React Hooks, writing functional components has become the standard way of writing React components in modern applications.
Example:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// Usage
const element = <Welcome name="World!" />;
With Hooks:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // State management via Hook
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
⚝ Try this example on CodeSandbox
Q. What is the difference between functional and class components in React?
Functional Components are plain JavaScript functions that accept props and return JSX. Since React 16.8, they support state and lifecycle behavior through Hooks.
Class Components are ES6 classes that extend React.Component. They have a render() method and manage state and lifecycle via this.
Syntax Comparison:
// Functional Component
function Greeting({ name }) {
const [clicked, setClicked] = useState(false);
return <h1 onClick={() => setClicked(true)}>Hello, {name}</h1>;
}
// Equivalent Class Component
class Greeting extends React.Component {
constructor(props) {
super(props);
this.state = { clicked: false };
}
render() {
return (
<h1 onClick={() => this.setState({ clicked: true })}>
Hello, {this.props.name}
</h1>
);
}
}
Key Differences:
| Feature | Functional Component | Class Component |
|---|---|---|
| Syntax | JavaScript function | ES6 class extending React.Component |
| State | useState() hook |
this.state + this.setState() |
| Lifecycle | useEffect() hook |
componentDidMount(), componentDidUpdate(), etc. |
this keyword |
Not used | Required to access props and state |
| Boilerplate | Minimal | More verbose |
| Performance | Slightly lighter (no class instance) | Slight overhead per instance |
| Error Boundaries | Not supported | Supported via getDerivedStateFromError |
| Modern recommendation | ✅ Preferred for all new code | Legacy (still valid for error boundaries) |
# 4.2. CLASS COMPONENTS
Q. What are class components in react?
Class components are stateful/container components that are regular ES6 classes extending React.Component. They're called stateful components because they:
- Control how state changes and implement component logic
- Have access to all React lifecycle methods
- Must have a
render()method that returns JSX
Example:
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = { message: "Hello!" };
}
render() {
return <h1>{this.state.message} {this.props.name}</h1>;
}
}
When to Use:
- Legacy Codebases: Maintaining existing projects built before 2019.
- Error Boundaries: Implementing components that catch JavaScript errors in their child tree, as these still require class-based lifecycle methods.
- Specific Third-Party Libraries: Interacting with older libraries that may require a class-based structure.
⚝ Try this example on CodeSandbox
Q. What is the recommended ordering of methods in class component?
- static methods
- constructor()
- getChildContext()
- componentWillMount()
- componentDidMount()
- componentWillReceiveProps()
- shouldComponentUpdate()
- componentWillUpdate()
- componentDidUpdate()
- componentWillUnmount()
- click handlers or event handlers like
onClickSubmit()oronChangeDescription() - getter methods for render like
getSelectReason()orgetFooterContent() - optional render methods like
renderNavigation()orrenderProfilePicture() - render()
This ordering helps maintain consistency and readability in your class components by grouping related methods together: lifecycle methods first, then event handlers, helper methods, and finally the render method at the bottom.
Q. How to create a dynamic table in react?
/**
* Generate dynamic table in React
*/
class Table extends React.Component {
constructor(props) {
super(props)
this.state = {
employees: [
{ id: 10, name: 'Swarna Sachdeva', email: 'swarna@email.com' },
{ id: 20, name: 'Sarvesh Date', email: 'sarvesh@email.com' },
{ id: 30, name: 'Diksha Meka', email: 'diksha@email.com' }
]
};
}
renderTableHeader() {
let header = Object.keys(this.state.employees[0])
return header.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>
})
}
renderTableData() {
return this.state.employees.map((employee, index) => {
const { id, name, email } = employee
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{email}</td>
</tr>
)
})
}
render() {
return (
<div>
<h1>Employee Records</h1>
<table>
<thead>
<tr>{this.renderTableHeader()}</tr>
</thead>
<tbody>{this.renderTableData()}</tbody>
</table>
</div>
)
}
}
⚝ Try this example on CodeSandbox
Q. How do you set a timer to update every second in React.js?
In React.js, the most reliable way to set a timer that updates every second is by using the useEffect() and useState() hooks together.
Core Implementation:
To ensure your timer starts correctly and doesn't cause memory leaks, follow these three steps:
- State: Store the timer value in a useState variable.
- Interval: Create the timer using setInterval inside a useEffect hook with an empty dependency array (
[]) so it only starts once when the component mounts. - Cleanup: Return a function that calls clearInterval to stop the timer when the component is removed (unmounted).
Example: Counting Up Every Second
import { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
// 1. Set the interval to 1000ms (1 second)
const interval = setInterval(() => {
// Use functional update to avoid closure issues with 'seconds'
setSeconds(prev => prev + 1);
}, 1000);
// 2. Clean up the interval on unmount
return () => clearInterval(interval);
}, []); // Empty array ensures this only runs once
return <div>Time elapsed: {seconds} seconds</div>;
}
Q. Differentiate between stateful and stateless components?
1. Stateless Components (Presentational)
Stateless components are simple functions that receive data and return what should be rendered. They do not have an internal “memory.”
- Definition: Often called “dumb” or “presentational” components.
- Data Source: They rely exclusively on props passed down from a parent.
- Implementation: Typically written as Functional Components.
- Logic: They focus on the UI and do not handle complex logic or lifecycle methods.
- Benefits: Highly reusable, easy to test, and predictable (pure functions).
2. Stateful Components (Container)
Stateful components manage and track their own internal data (state), which can change based on user interaction or API responses.
- Definition: Often called “smart” or “container” components.
- Data Source: They use internal state (via this.state in classes or useState in functions) and can also use props.
- Implementation: Traditionally written as Class Components, but now commonly created using Hooks in Functional Components.
- Logic: They handle business logic, data fetching, and user events that update the UI.
- Benefits: Essential for interactive elements like forms, counters, and toggles.
Example: Stateless/Presentational/Dumb component
import React from 'react';
// This is stateless: it just takes a name and displays it.
const Greeting = ({ name }) => {
return <h1>Hello, {name}!</h1>;
};
export default Greeting;
Example: Stateful/Container/Smart component
import React, { useState } from 'react';
const ClickCounter = () => {
// 'count' is the state, 'setCount' is the function to update it.
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
};
export default ClickCounter;
Quick Comparison Table:
| Feature | Stateless Components | Stateful Components |
|---|---|---|
| Internal State | No | Yes |
| Knowledge | Knows nothing of the past/future | Remembers past changes |
| Primary Tool | props | state (and props) |
| Complexity | Simple & lightweight | Complex logic-heavy |
| Testing | Easy to test | Harder to test |
Q. What is the difference between Element, Component and Component instance in React?
In React, these three concepts represent different levels of abstraction:
1. React Elements:
A React Element is just a plain old JavaScript Object without own methods. It has essentially four properties:
- type: a String representing an HTML tag or a reference referring to a React Component
- key: a String to uniquely identify an React Element
- ref: a reference to access either the underlying DOM node or React Component Instance
- props: (properties Object)
A React Element is not an instance of a React Component. It is just a simplified “description” of how the React Component Instance to be created should look like.
Example:
const element = <h1>Hello</h1>;
// Same as: React.createElement('h1', null, 'Hello')
2. React Components and React Component Instances:
A React Component is used by extending React.Component. If a React Component is instantiated it expects a props Object and returns an instance, which is referred to as a React Component Instance.
A React Component can contain state and has access to the React Lifecycle methods. It must have at least a render method, which returns a React Element(-tree) when invoked.
Example:
// Function Component
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// Class Component
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
Q. What does shouldComponentUpdate do and why is it important?
The shouldComponentUpdate() method allows Component to exit the Update life cycle if there is no reason to apply a new render. It receives nextProps and nextState as parameters and returns a boolean.
Example:
shouldComponentUpdate(nextProps, nextState) {
console.log(nextProps, nextState)
console.log(this.props, this.state)
return false // Prevents re-render
}
Why it is Important
-
Performance Optimization: It's the first lifecycle optimization method you can leverage in React to prevent unnecessary renders.
-
Prevents Wasteful Updates: By default, React re-renders whenever props or state change, even if the values are effectively the same. This method lets you compare current and next values to avoid redundant renders
-
Manual Control: While React doesn't deeply compare props by default,
shouldComponentUpdate()gives you fine-grained control over when updates happen
Q. What is the purpose of render() function in React?
In React, the render() function is a core lifecycle method used primarily in class components to define the visual structure of the user interface (UI). It acts as a “blueprint” that tells React what elements should appear on the screen based on the component's current data
Key purposes:
- Returns UI elements - Displays specified HTML/JSX inside a DOM element
- Reads props and state - Accesses component data to determine what to render
- Pure function - Cannot modify state or cause side effects (like HTTP requests)
- Required method - Every class component must have a render() method
Example:
/**
* render() function
*
*/
class App extends React.Component {
render() {
return <h1>Render() Method Example</h1>;
}
}
The render() method is called automatically whenever props or state change, causing React to update the DOM with the new output.
Note:
The modern React (v18+), functional components use return directly instead of a render() method, and the actual DOM rendering is done via createRoot().render().
⚝ Try this example on CodeSandbox
# 4.2.1. REACT LIFECYCLE
Q. What are the different phases of React component lifecycle?
React provides several methods that notify us when certain stage of this process occurs. These methods are called the component lifecycle methods and they are invoked in a predictable order. The lifecycle of the component is divided into three main phases.
1. Mounting:
These methods are called in the following order when an instance of a component is being created and inserted into the DOM:
constructor()getDerivedStateFromProps()render()componentDidMount()
2. Updating:
The next phase in the lifecycle is when a component is updated. A component is updated whenever there is a change in the component's state or props.
React has five built-in methods that gets called, in this order, when a component is updated:
getDerivedStateFromProps()shouldComponentUpdate()render()getSnapshotBeforeUpdate()componentDidUpdate()
3. Unmounting:
The next phase in the lifecycle is when a component is removed from the DOM, or unmounting as React likes to call it.
componentWillUnmount()
Q. How to make component to perform an action only once when the component initially rendered?
1. Using Class Component:
The componentDidMount() lifecycle hook can be used with class components. Any actions defined within a componentDidMount() lifecycle hook are called only once when the component is first mounted.
Example:
class Homepage extends React.Component {
componentDidMount() {
trackPageView('Homepage') // Runs only once after first render
}
render() {
return <div>Homepage</div>
}
}
2. Using Functional Component:
The useEffect() hook can be used with function components. The useEffect() hook is more flexible than the lifecycle methods used for class components. It receives two parameters:
- The first parameter it takes is a callback function to be executed.
- The optional second parameter it takes is an array containing any variables that are to be tracked.
The value passed as the second argument controls when the callback is executed:
- If the second parameter is undefined, the callback is executed every time that the component is rendered.
- If the second parameter contains an array of variables, then the callback will be executed as part of the first render cycle and will be executed again each time an item in the array is modified.
- If the second parameter contains an empty array, the callback will be executed only once as part of the first render cycle.
Example:
const Homepage = () => {
useEffect(() => {
trackPageView('Homepage') // Runs only once after first render
}, []) // Empty dependency array is the key!
return <div>Homepage</div>
}
Q. What is the typical pattern for rendering a list of components from an array of data?
The typical pattern for rendering a list of components from an array of data in React is to use the .map() method:
Example:
const fruits = ['Apple', 'Banana', 'Cherry'];
function FruitList() {
return (
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
);
}
Q. What is difference between useEffect() vs componentDidMount()?
In react when we use class based components we get access to lifecycle methods ( like componentDidMount(), `componentDidUpdate(), etc ). But when we want use a functional component and also we want to use lifecycle methods, then using useEffect() we can implement those lifecycle methods.
1. componentDidMount():
The componentDidMount() and useEffect() run after the mount. However useEffect() runs after the paint has been committed to the screen as opposed to before. This means we would get a flicker if needed to read from the DOM, then synchronously set state to make new UI.
The useLayoutEffect() was designed to have the same timing as componentDidMount(). So useLayoutEffect(fn, []) is a much closer match to componentDidMount() than useEffect(fn, []) – at least from a timing standpoint.
/**
* componentDidMount() in Class Component
*/
import React, { Component } from "react";
export default class SampleComponent extends Component {
componentDidMount() {
// code to run on component mount
}
render() {
return <>componentDidMount Example</>;
}
}
2. useEffect():
/**
* useEffect() in Functional Component
*/
import React, { useEffect } from "react";
const SampleComponent = () => {
useEffect(() => {
// code to run on component mount
}, []);
return <>useEffect Example</>;
};
export default SampleComponent;
When useEffect() is used to get data from server.
- The first argument is a callback that will be fired after browser layout and paint. Therefore it does not block the painting process of the browser.
- The second argument is an array of values (usually props).
- If any of the value in the array changes, the callback will be fired after every render.
- When it is not present, the callback will always be fired after every render.
- When it is an empty list, the callback will only be fired once, similar to componentDidMount.
Q. Why is a component constructor called only once?
React's reconciliation algorithm assumes that without any information to the contrary, if a custom component appears in the same place on subsequent renders, it's the same component as before, so reuses the previous instance rather than creating a new one.
Key Concept: React uses component position in the tree to determine identity. If a component renders in the same spot, React keeps the same instance alive.
How to Force a New Instance:
If you give each component a unique key prop, React can use the key change to infer that the component has actually been substituted and will create a new one from scratch, giving it the full component lifecycle.
Example:
renderContent() {
if (this.state.activeItem === 'item-one') {
return (
<ComponentA title="First" key="first" />
)
} else {
return (
<ComponentA title="Second" key="second" />
)
}
}
When the key changes, React recognizes it as a different component and will:
- Unmount the old instance
- Create a new instance from scratch
- Call the constructor again
- Go through the full component lifecycle
This is why constructors are called only once per component instance - React optimizes by reusing instances when possible, unless you explicitly signal otherwise with the key prop.
Q. What is difference between componentDidMount() and componentWillMount()?
componentDidMount():
The componentDidMount() is executed after the first render only on the client side. This is where AJAX requests and DOM or state updates should occur. This method is also used for integration with other JavaScript frameworks and any functions with delayed execution such as setTimeout() or setInterval().
Example:
class App extends Component {
componentDidMount() {
this.getData() // API calls go here
}
getData() {
setTimeout(() => {
this.setState({ data: 'Data loaded!' })
}, 1000)
}
}
componentWillMount():
The componentWillMount() method is executed before rendering, on both the server and the client side. componentWillMount() method is the least used lifecycle method and called before any HTML element is rendered. It is useful when we want to do something programmatically right before the component mounts.
Example:
class App extends Component {
componentWillMount() {
console.log('First this called') // Runs before render
}
componentDidMount() {
this.getData() // Runs after render
}
}
Note:
componentWillMount() is now deprecated in modern React. You should avoid using it and use componentDidMount() or the constructor() instead for initialization logic.
Q. Is it good to use setState() in componentWillMount() method?
Avoid async initialization in componentWillMount().
componentWillMount() is invoked immediately before mounting occurs. It is called before render(), therefore setting state in this method will not trigger a re-render. Avoid introducing any side-effects or subscriptions in this method.
Make async calls for component initialization in componentDidMount() instead of componentWillMount()
function componentDidMount() {
axios.get(`api/messages`)
.then((result) => {
const messages = result.data
console.log("COMPONENT WILL Mount messages : ", messages);
this.setState({
messages: [...messages.content]
})
})
}
Q. How to use componentWillUnmount() with Functional Components in React?
The useEffect() can be used to manage API calls, as well as implementing componentWillMount(), and componentWillUnmount().
If we pass an empty array as the second argument, it tells useEffect to fire on component load. This is the only time it will fire.
import React, { useEffect } from 'react';
const ComponentExample = () => {
useEffect(() => {
console.log('Component mounted');
// This runs once on mount
}, []); // Empty array = runs only on mount
}
If you add a return function inside the useEffect() function, it is triggered when a component unmounts from the DOM.
import React, { useEffect } from 'react';
const ComponentExample = () => {
useEffect(() => {
console.log('Component mounted');
// Return a cleanup function
return () => {
console.log('Component will unmount');
// Cleanup: remove listeners, cancel subscriptions, etc.
};
}, []); // Empty array ensures cleanup runs only on unmount
}
# 5. REACT PROPS
Q. What is props in React?
In React, props (short for “properties”) are read-only objects used to pass data from a parent component to a child component. However, callback functions can also be passed, which can be executed inside the child to initiate an update.
Props are immutable so we cannot modify the props from inside the component. These attributes are available in the class component as this.props and can be used to render dynamic data in our render method.
Characteristics of Props
- Unidirectional Data Flow: Data always flows in one direction—from parent to child.
- Immutability: Props are read-only. A child component cannot modify the props it receives; it can only consume them to render content or trigger logic.
- Dynamic Content: By using props, you can make a single component reusable by passing different data to it each time it is rendered.
- Any Data Type: You can pass strings, numbers, booleans, objects, arrays, and even functions (callbacks) as props
Example:
// Receiving Props
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// Passing Props
const element = <Welcome name="Pradeep!" />;
Example: Destructuring
function Welcome({ name, age }) {
return <h1>Hello, {name}. You are {age} years old.</h1>;
}
Special Props
- children: A built-in prop that represents whatever content is placed between the opening and closing tags of a component.
- Default Props: You can set fallback values for props in case the parent doesn't provide them.
⚝ Try this example on CodeSandbox
Q. Why props in React are read only?
In React, props are read-only because components are designed to act like pure functions. This means they should not attempt to change their inputs (props) and must always return the same JSX output for the same set of props.
Here are the primary reasons why this rule is strictly enforced:
1. Unidirectional Data Flow
React follows a one-way data flow, where data travels from parent to child. If a child component could modify its own props, it would be effectively changing the state of its parent without the parent's knowledge. This would make the application's data flow unpredictable and extremely difficult to trace.
2. Predictability and Debugging
When props are immutable, you can be certain that a component's data won't change unexpectedly within its own scope. If a bug occurs, you only need to look “up” the component tree to the parent to find where the data originated, rather than checking every child that might have modified it.
3. Performance Optimization
React is highly efficient because it uses shallow reference equality to decide if a component needs to re-render.
- If you could mutate props directly (e.g., props.user.name = ‘New Name’), the reference to the props object would remain the same.
- Because the reference hasn't changed, React might assume nothing is different and skip necessary UI updates.
4. Clear Separation of Concerns
A component should only be responsible for managing its own internal state. Props represent the “external interface” or the configuration provided by the outside world. Keeping them read-only ensures that components remain reusable and independent of where they are used.
⚝ Try this example on CodeSandbox
Q. What are default props?
The defaultProps is a React component property that allows you to set default values for the props argument. If the prop property is passed, it will be changed.
The defaultProps can be defined as a property on the component class itself to set the default props for the class. defaultProps is used for undefined props, not for null props.
/**
* Default Props
*/
class MessageComponent extends React.Component {
render() {
return (
<div>Hello, {this.props.value}.</div>
)
}
}
// Pass default Props
MessageComponent.defaultProps = {
value: 'World'
}
For function components, you can use default values in destructuring:
Example:
function Greeting({ name = "Guest" }) {
return <h1>Hello, {name}!</h1>;
}
⚝ Try this example on CodeSandbox
Q. How to access props inside quotes in React JSX?
React JSX doesn't support variable interpolation inside an attribute value, but we can put any JS expression inside curly braces as the entire attribute value.
1. Concatenation
<img src={"images/" + this.props.image} />
2. Template literals
<img src={`images/${this.props.image}`} />
Example:
/**
* Access Props
*/
class App extends Component {
render() {
return (
<div>
<img
alt="React Logo"
// Using ES6 template literals
src={`${this.props.image}`}
/>
</div>
);
}
}
export default App;
⚝ Try this example on CodeSandbox
Q. How to pass numbers to React component?
In react, numbers can be passed via curly braces({}) where as strings in quotes ("");
Example:
function App() {
return <Greetings name="Pradeep" age={28} occupation="Software Developer" />;
}
// Greetings Component
function Greetings(props) {
return (
<h2>
Hello! I'm {props.name}, a {props.age} years old {props.occupation}.
Pleased to meet you!
</h2>
);
}
⚝ Try this example on CodeSandbox
Q. How are boolean props used in React?
Boolean props in React are used to pass true/false values to components. In JSX:
- To pass true:
<MyComponent prop />or<MyComponent prop={true} /> - To pass false:
<MyComponent prop={false} />
Example:
/**
* Boolean Props
*/
const MyComponent = ({ prop1, prop2 }) => (
<div>
<div>Prop1: {String(prop1)}</div>
<div>Prop2: {String(prop2)}</div>
</div>
)
function App() {
return (
<div>
<MyComponent prop1={true} prop2={false} />
<MyComponent prop1 prop2 />
<MyComponent prop1={false} prop2 />
</div>
);
}
⚝ Try this example on CodeSandbox
Q. How to apply validation on Props in React?
To apply validation on props in React, you can use either the PropTypes library for runtime checks or TypeScript for static type-checking during development
Example: Using PropTypes (Runtime Validation)
/**
* Props Validation
*/
import PropTypes from 'prop-types';
function MyComponent({ name, age }) {
return <h1>{name} is {age}</h1>;
}
MyComponent.propTypes = {
name: PropTypes.string.isRequired, // Must be a string and is mandatory
age: PropTypes.number // Optional number
};
Example: Using TypeScript (Static Validation)
interface UserProps {
name: string;
age?: number; // The '?' makes it optional
}
const MyComponent: React.FC<UserProps> = ({ name, age }) => {
return <h1>{name} is {age}</h1>;
};
⚝ Try this example on CodeSandbox
Q. How to specify the shape of an object with PropTypes
The PropTypes.shape() validator can be used when describing an object whose keys are known ahead of time, and may represent different types.
Example:
/**
* PropTypes.shape()
* @param {*} props
*/
import PropTypes from "prop-types";
const Component = (props) => (
<div>
Component badge: {props.badge ? JSON.stringify(props.badge) : "none"}
</div>
);
// PropTypes validation for the prop object
Component.propTypes = {
badge: PropTypes.shape({
src: PropTypes.string.isRequired,
alt: PropTypes.string.isRequired
})
};
const App = () => (
<div>
<Component badge= />
{/*<Component badge=/>*/}
<Component />
</div>
);
export default App;
Output:
Component badge: {"src":"horse.png","alt":"Running Horse"}
Component badge: none
⚝ Try this example on CodeSandbox
Q. How PropTypes.objectOf is different from PropTypes.shape?
PropTypes.shape checks for a specific structure (keys and their types) in an object. Each key can have a different type.
PropTypes.objectOf checks that all values in the object are of a specific type, but does not enforce specific keys.
Example:
PropTypes.shape({ name: PropTypes.string, age: PropTypes.number })
→ Expects an object with keys name (string) and age (number).
PropTypes.objectOf(PropTypes.string)
→ Expects an object where all values are strings, regardless of the keys.
/**
* PropTypes
*/
import PropTypes from 'prop-types';
// Expected prop object - dynamic keys (i.e. user ids)
const myProp = {
25891102: 'Shila Jayashri',
34712915: 'Employee',
76912999: 'shila.jayashri@email.com'
};
// PropTypes validation for the prop object
MyComponent.propTypes = {
myProp: PropTypes.objectOf(PropTypes.number)
};
Q. How React PropTypes allow different types for one prop?
Using PropTypes.oneOfType() says that a prop can be one of any number of types. For instance, a phone number may either be passed to a component as a string or an integer:
/**
* PropTypes.oneOfType()
*/
const Component = (props) => <div>Phone Number: {props.phoneNumber}</div>
Component.propTypes = {
phoneNumber: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string
]),
}
const App = () => (
<div>
<Component phoneNumber={04403472916}/>
{/*<Component phoneNumber={"2823788557"}/>*/}
</div>
);
⚝ Try this example on CodeSandbox
Q. What are render props?
Render props are a React pattern where a component accepts a function as a prop, and calls that function to determine what to render.
The function receives data or state from the component and returns React elements. This enables sharing logic between components.
Example:
/**
* Render Props
*/
import Wrapper from "./Wrapper";
class App extends React.Component {
render() {
return (
<Wrapper
render={({ increment, count }) => (
<div>
<h3>Render Props Counter</h3>
<p>{count}</p>
<button onClick={() => increment()}>Increment</button>
</div>
)}
/>
);
}
}
/**
* Wrapper Component
*/
class Wrapper extends React.Component {
state = {
count: 0
};
// Increase count
increment = () => {
const { count } = this.state;
return this.setState({ count: count + 1 });
};
render() {
const { count } = this.state;
return (
<div>
{this.props.render({ increment: this.increment, count: count })}
</div>
);
}
}
Important Caution: If you define a render prop function inline (inside the render method), it creates a new function on every render, which can cause performance issues by triggering unnecessary re-renders in child components.
⚝ Try this example on CodeSandbox
Q. What are the benefits of using Render Props?
The benefits of using Render Props in React are:
- Code Reusability: Share logic between components without duplicating code.
- Flexibility: Easily customize what gets rendered by passing different render functions.
- Isolation: State and logic are encapsulated in the render prop component, avoiding naming collisions.
- No Static Method Hoisting: No need to hoist static methods as with HOCs.
- Clear Data Flow: It's explicit which data and methods are shared, making the code easier to understand and maintain.
Problems:
- Caution using
shouldComponentUpdate()as the render prop might close over data it is unaware of. - There could also be minor memory issues when defining a closure for every render. But be sure to measure first before making performance changes as it might not be an issue for your app.
- Another small annoyance is the render props callback is not so neat in JSX as it needs to be wrapped in an expression. Rendering the result of an HOC does look cleaner.
Q. How do you create Higher Order Component using render props?
You can create a Higher Order Component (HOC) using render props by combining the two patterns: the HOC returns a component that uses a render prop to share logic or state with the wrapped component. Here is how you do it:
- Define a function (the HOC) that takes a component as an argument.
- Inside, return a new component that renders the original component, passing it data or methods via props.
- Use a render prop internally to provide the shared logic or state.
Example:
// HOC using render props
function withMouse(Component) {
return class extends React.Component {
render() {
return (
<Mouse
render={mouse => <Component {...this.props} mouse={mouse} />}
/>
);
}
};
}
// Render prop component
class Mouse extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = e => {
this.setState({ x: e.clientX, y: e.clientY });
};
render() {
return (
<div style= onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
// Usage
const Cat = ({ mouse }) => (
<img src="cat.png" style= />
);
const CatWithMouse = withMouse(Cat);
Q. Explain HOC vs render props in react.js?
1. Higher-Order Component (HOC):
- An HOC is a function that takes a component and returns a new component with enhanced behavior or additional props.
- It works like a decorator, wrapping the original component.
Example:
function withLogger(WrappedComponent) {
return function(props) {
console.log('Props:', props);
return <WrappedComponent {...props} />;
};
}
2. Render Props:
- A render prop is a function prop that a component uses to know what to render.
- The parent component provides a function as a prop, and the child calls it, passing in data or state.
Example:
<DataProvider render={data => <Display data={data} />} />
The render function receives data and returns React elements.
Key Differences:
- HOC wraps a component and returns a new one; Render Props passes a function to a component to control rendering.
- HOC is more like composition via wrapping; Render Props is composition via function-as-child.
- Render Props can be more flexible and explicit, but may lead to “wrapper hell” if overused.
Q. What is children props?
The {this.props.children} is a special prop, automatically passed to every component, that can be used to render the content included between the opening and closing tags when invoking a component.
Example:
/**
* React Children Props
*/
function MyComponent(props) {
return <div>{props.children}</div>;
}
// Usage:
<MyComponent>
<p>This is a child element.</p>
<AnotherComponent />
</MyComponent>
Here, props.children will contain the <p> and <AnotherComponent /> elements.
Key points:
- Enables component composition and nesting.
- Can be a single element, multiple elements, or even text.
- Used for flexible layouts and reusable wrappers.
⚝ Try this example on CodeSandbox
Q. When we should use React.cloneElement vs this.props.children?
1. React.cloneElement():
Use React.cloneElement() when you need to modify or add props to a child element before rendering it, such as injecting additional data, event handlers, or styling. This is useful for component composition where the parent wants to enhance or control its children.
2. this.props.Children():
Use this.props.children when you simply want to render the children passed to a component without modification. It's the default way to render nested elements in React.
Example: Using this.props.children
/**
* React Children Props
*/
function Parent({ children }) {
return <div>{children}</div>;
}
// Usage:
<Parent>
<button>Click me</button>
</Parent>
The button is rendered as-is.
Example: Using React.cloneElement
/**
* React Clone Element
*/
function Parent({ children }) {
return React.cloneElement(children, { style: { color: 'red' } });
}
// Usage:
<Parent>
<button>Click me</button>
</Parent>
The button is rendered with the additional style prop (text color red)
Summary:
- Use
React.cloneElementfor modify/add props to children before rendering. - Use
this.props.childrenfor direct rendering without changes.
⚝ Try this example on CodeSandbox
Q. What do these three dots in React do?
In React, the three dots (…) are most commonly known as the Spread Operator. They are used to “spread” or expand the contents of an object or array into a new one.
1. Spread props: Pass all properties of an object as props to a component.
Example: This spreads all properties from the props object into the Profile component, so Profile receives name and age as props.
const props = { name: "Alice", age: 25 };
function Profile(props) {
return <div>{props.name}, {props.age}</div>;
}
// Usage:
<Profile {...props} />
2. Merging Arrays or Objects: Create shallow copies or merge objects/arrays.
Example:
const [user, setUser] = useState({ name: "Alice", age: 25 });
const updates = { age: 26, city: "NY" };
setUser({ ...user, ...updates }); // Result: { name: "Alice", age: 26, city: "NY" }
3. Rest Parameters: In destructuring, ...rest gathers the remaining properties into a new object.
Example: Here, label is extracted from props, and all other props (type, className, etc.) are collected in rest and spread onto the <button> element.
function Button({ label, ...rest }) {
return <button {...rest}>{label}</button>;
}
// Usage:
<Button label="Click Me" type="submit" className="primary" />
The spread operator is a JavaScript feature, not React-specific, but it's commonly used in React for props and state management.
Q. Why we need to be careful when spreading props on DOM elements?
Spreading props directly onto DOM elements (e.g., <div {...props} />) is often considered an anti-pattern because it bypasses explicit control over what attributes are rendered to the browser.
-
Unknown Prop Warnings: Spreading props may pass non-standard or custom props to DOM elements, which can result in React warnings or browser errors, since the browser doesn't recognize these attributes.
-
Security risks: Sensitive data or event handlers could unintentionally be exposed or attached to the DOM, increasing the risk of XSS or leaking information.
-
Performance issues: Passing unnecessary props can clutter the DOM and slow down rendering.
-
Maintainability: It becomes harder to track which props are actually used, making debugging and refactoring more difficult.
To avoid these issues, always filter props and only pass valid attributes to DOM elements.
Example: By creating props specifically for DOM attribute, we can safely spread.
const Sample = () => (<Spread flag={true} domProps=/>);
const Spread = (props) => (<div {...props.domProps}>Test</div>);
Or alternatively we can use prop destructuring with ...rest:
const Sample = () => (<Spread flag={true} className="content"/>);
const Spread = ({ flag, ...domProps }) => (<div {...domProps}>Test</div>);
Note:
In scenarios where you use a PureComponent, when an update happens it re-renders the component even if domProps did not change. This is because PureComponent only shallowly compares the objects.
Q. What will happen if you use props in initial state?
Using props to generate state in getInitialState often leads to duplication of “source of truth”, i.e. where the real data is. This is because getInitialState is only invoked when the component is first created.
The danger is that if the props on the component are changed without the component being ‘refreshed’, the new prop value will never be displayed because the constructor function (or getInitialState) will never update the current state of the component. The initialization of state from props only runs when the component is first created.
Bad:
The below component won't display the updated input value
class App extends React.Component {
// constructor function (or getInitialState)
constructor(props) {
super(props)
this.state = {
records: [],
inputValue: this.props.inputValue
}
}
render() {
return <div>{this.state.inputValue}</div>
}
}
Good:
Using props inside render method will update the value:
class App extends React.Component {
// constructor function (or getInitialState)
constructor(props) {
super(props)
this.state = {
records: []
}
}
render() {
return <div>{this.props.inputValue}</div>
}
}
Q. What is the difference between createElement and cloneElement?
React.createElement is used to create a new React element from scratch. It's what JSX compiles to under the hood. It is used when you want to define what to render for the first time.
React.cloneElement is used to clone and return a copy of an existing React element, with new props or children merged in. It's useful when you want to take an existing element (often passed as children) and add or override some props before rendering.
Example: React.createElement
import { createElement } from 'react';
function SimpleBanner() {
// Equivalent to <h1 className="banner">Welcome!</h1>
return createElement(
'h1',
{ className: 'banner' },
'Welcome!'
);
}
Example: React.cloneElement
import React, { Children, cloneElement } from 'react';
function RadioGroup({ name, children }) {
return (
<div className="radio-group">
{/*
We map through children and "clone" each one,
merging the 'name' prop into them.
*/}
{Children.map(children, (child) => {
if (React.isValidElement(child)) {
return cloneElement(child, { name });
}
return child;
})}
</div>
);
}
// Usage:
// Even though we did not add the 'name' prop to each <input />,
// RadioGroup will inject it into both clones.
function App() {
return (
<RadioGroup name="theme-selection">
<input type="radio" value="light" /> Light
<input type="radio" value="dark" /> Dark
</RadioGroup>
);
}
Note:
The React documentation warns that overusing cloneElement can make data flow harder to trace. In many cases, using render props or Context is preferred for passing data down the tree.
Q. How to pass JSON Objects from Child to Parent Component?
To pass JSON objects (or any data) from a child to a parent component in React, you use a callback function. The parent defines a function and passes it as a prop to the child. The child calls this function, passing the JSON object as an argument.
Example:
/**
* Parent Component
*/
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { dataFromChild: null };
this.handleData = this.handleData.bind(this);
}
handleData(jsonObj) {
this.setState({ dataFromChild: jsonObj });
}
render() {
return (
<div>
<Child sendData={this.handleData} />
<pre>{JSON.stringify(this.state.dataFromChild, null, 2)}</pre>
</div>
);
}
}
/**
* Child Component
*/
class Child extends React.Component {
sendJsonToParent = () => {
const jsonObj = { name: "Pradeep Kumar", age: 28 };
this.props.sendData(jsonObj);
};
render() {
return <button onClick={this.sendJsonToParent}>Send JSON to Parent</button>;
}
}
⚝ Try this example on CodeSandbox
Q. What is the use of this.props?
In React, the expression {...this.props} is called the “spread operator” for props. Its purpose is to pass all the props received by a component down to a child or DOM element, making it easier to forward or reuse props without listing each one individually.
Example:
<div {...this.props}>Content Here</div>
It is equal to Class Component
const person = {
name: "Pradeep",
age: 26,
country: "India"
}
class SpreadExample extends React.Component {
render() {
const {name, age, country} = {...this.props}
return (
<div>
<h3>Person Information:</h3>
<ul>
<li>name={name}</li>
<li>age={age}</li>
<li>country={country}</li>
</ul>
</div>
)
}
}
# 6. REACT STATE
Q. What is State in React?
In React, state is a built-in object that stores data or information about the component. State allows a component to keep track of changing information between renders. When the state of a component changes, React automatically re-renders the component to reflect the new state.
Key Characteristics of State:
- State is local to the component and controlled by the component itself.
- State can be changed using the
setState()method (in class components) or theuseState()hook (in function components). - Changing state triggers a re-render of the component and its children.
- State is used for dynamic data that can change over time, such as user input, toggles, counters, etc.
Example:
/**
* React State
*/
import { useState } from 'react';
function Counter() {
// Declares 'count' state variable and 'setCount' updater function
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
⚝ Try this example on CodeSandbox
Q. What does setState() do?
In React, the setState() method is used to update the state of a class component. When you call setState(), React schedules a re-render of the component with the new state values. This allows your UI to update in response to user actions or other events. setState() is asynchronous and can accept either an object with updated state values or a function that returns the new state based on the previous state.
Example:
/**
* React setState()
*/
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
color: "blue"
};
}
handleColor = () => {
this.setState({ color: "red" });
};
render() {
return (
<div>
<h1>Color: {this.state.color}</h1>
<button type="button" onClick={this.handleColor}>Change Color</button>
</div>
);
}
}
Note:
In modern functional components, developers use the useState() Hook. While it serves the same purpose, there is a key difference: the “set” function from useState replaces the state instead of merging it
⚝ Try this example on CodeSandbox
Q. Why is setState() in React async instead of sync?
In React, setState() is asynchronous instead of synchronous to optimize performance and ensure a smooth user experience. When you call setState(), React does not immediately update the state and re-render the component. Instead, it batches multiple state updates together and schedules a re-render for later, often after the current event loop finishes.
This batching helps avoid unnecessary renders and keeps the UI responsive, especially when many state updates happen in quick succession (like during user input or network events). If setState() were synchronous, each update would trigger an immediate re-render, which could make the app slow and unresponsive.
Q. What is the second argument that can optionally be passed to setState() and what is its purpose?
The second argument to setState() in React Class Component is an optional callback function. Its purpose is to execute code after the state has been updated and the component has re-rendered. This is useful when you need to perform actions that depend on the updated state or the DOM being updated
Example:
/**
* setState() Callback
*/
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 }, () => {
console.log('setState has finished and the component has re-rendered.');
console.log('Updated count:', this.state.count);
});
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
export default Counter;
The setState() will always lead to a re-render unless shouldComponentUpdate() returns false. To avoid unnecessary renders, calling setState() only when the new state differs from the previous state makes sense and can avoid calling setState() in an infinite loop within certain lifecycle methods like componentDidUpdate().
Q. What are the possible ways of updating objects in state?
Instead of directly modifying the state using this.state(), we use this.setState(). This is a function available to all React components that use state, and allows us to let React know that the component state has changed. This way the component knows it should re-render, because its state has changed and its UI will most likely also change.
Example:
1. Using Object.assign():
Create a copy of the object, update the property, and return the new object:
this.setState(prevState => {
let user = Object.assign({}, prevState.user); // creating copy of state variable user
user.name = 'New Name'; // update the name property, assign a new value
return { user };
})
2. Using spread Operator(...)
Spread the previous object and override the property you want to update:
this.setState(prevState => ({
user: {
...prevState.user, // keep all other key-value pairs
name: 'New Name' // update the value of specific key
}
}))
3. For Nested Objects:
Spread each level to avoid mutation:
this.setState(prevState => ({
user: {
...prevState.user,
address: {
...prevState.user.address,
city: 'New City'
}
}
}));
4. Functional Updates (with Hooks):
For functional components using useState():
setUser(prevUser => ({
...prevUser,
name: 'New Name'
}));
Note:
Never mutate state directly. Always create a new object/array and update state with the new value. This ensures React can detect changes and re-render correctly.
⚝ Try this example on CodeSandbox
Q. What will happen if you use setState() in constructor?
If you use setState() in the constructor of a React class component, it will not work as expected. The constructor is meant for direct state assignment using this.state = {...}. Calling setState() in the constructor does not update the state and does not trigger a re-render, because the component has not yet been mounted. React expects initial state to be set directly, not via setState(), inside the constructor.
Best practice:
- Use
this.state = {...}in the constructor for initial state. - Use
this.setState()only after the component has mounted (e.g., in lifecycle methods or event handlers).
Example:
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 }; // Correct way
// Incorrect: setState() should NOT be used here
this.setState({ count: 1 }); // This will be ignored!
}
render() {
return <div>Count: {this.state.count}</div>;
}
}
export default Counter;
⚝ Try this example on CodeSandbox
Q. How to delete an item from state array?
To delete an item from a state array in React, you should never mutate the state directly. Instead, use the Array.prototype.filter() method to create a new array without the item you want to remove, and then update the state with this new array.
Example:
onDeleteByIndex(index) {
this.setState({
users: this.state.users.filter((item, i) => i !== index)
});
}
⚝ Try this example on CodeSandbox
Q. Why should not call setState() in componentWillUnmount()?
We should not call setState() in componentWillUnmount() because the component will never be re-rendered. Once a component instance is unmounted, it will never be mounted again.
The componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. This method can be used to perform any necessary cleanup method, such as invalidating timers, canceling network requests, or cleaning up any subscriptions that were created in componentDidMount().
Q. How can you re-render a component without using setState() function?
React components automatically re-render whenever there is a change in their state or props. A simple update of the state, from anywhere in the code, causes all the User Interface (UI) elements to be re-rendered automatically.
However, there may be cases where the render() method depends on some other data. After the initial mounting of components, a re-render will occur.
1. Using forceUpdate() (Class Component):
The following example generates a random number whenever it loads. Upon clicking the button, the forceUpdate() function is called which causes a new, random number to be rendered:
Example:
/**
* forceUpdate()
*/
export default class App extends React.Component {
constructor(){
super();
this.forceUpdateHandler = this.forceUpdateHandler.bind(this);
};
forceUpdateHandler(){
this.forceUpdate();
};
render(){
return(
<div>
<button onClick= {this.forceUpdateHandler} >FORCE UPDATE</button>
<h4>Random Number : { Math.random() }</h4>
</div>
);
}
}
Caution: This method is discouraged because it bypasses shouldComponentUpdate and can lead to performance issues
2. Using useReducer() (Functional Components):
The most common way to “force” a re-render in functional components is to use a useReducer hook as a simple toggle or counter.
Example:
import { useReducer } from 'react';
function MyComponent() {
// A simple counter that increments every time forceUpdate is called
const [, forceUpdate] = useReducer((x) => x + 1, 0);
const handleClick = () => {
// This triggers a re-render without meaningful state data
forceUpdate();
};
return <button onClick={handleClick}>Force Re-render</button>;
}
3. Changing the key Prop (Remounting):
By changing the key prop on a component, you force React to treat it as a brand-new element. This unmounts the old version and mounts a fresh instance, effectively resetting everything.
Example:
function Parent() {
const [resetKey, setResetKey] = useState(0);
return (
<>
<MyForm key={resetKey} />
<button onClick={() => setResetKey(prev => prev + 1)}>Reset Form</button>
</>
);
}
4. Custom Hook with useState():
You can also create a reusable custom hook that uses a “dummy” state update to trigger a re-render
Example:
function useForceUpdate() {
const [, setValue] = useState(0);
return () => setValue(value => value + 1); // update state to force render
}
⚝ Try this example on CodeSandbox
Q. Why we need to pass a function to setState()?
The reason behind for this is that setState() is an asynchronous operation. React batches state changes for performance reasons, so the state may not change immediately after setState() is called. That means we should not rely on the current state when calling setState().
The solution is to pass a function to setState(), with the previous state as an argument. By doing this we can avoid issues with the user getting the old state value on access due to the asynchronous nature of setState().
Problem:
// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3
⚝ Try this example on CodeSandbox
Solution:
this.setState((prevState) => ({
count: prevState.count + 1
}));
this.setState((prevState) => ({
count: prevState.count + 1
}));
this.setState((prevState) => ({
count: prevState.count + 1
}));
// this.state.count === 3 as expected
⚝ Try this example on CodeSandbox
Q. How to update nested state properties in React.js?
To update nested state properties in React.js, you should create a new copy of the nested object or array you want to update, modify it, and then set the new state. This ensures immutability, which is important for React to detect changes and re-render correctly.
Example:
const [user, setUser] = useState({
name: "Pradeep",
address: {
city: "Ranchi",
zip: "560076"
}
});
To update the city:
setUser(prevUser => ({
...prevUser,
address: {
...prevUser.address,
city: "Bengaluru"
}
}));
⚝ Try this example on CodeSandbox
Q. What is the difference between state and props?
Props are read-only, immutable data passed from parent to child components. They allow components to receive data and functions from their parent, enabling reusability and unidirectional data flow. Props cannot be changed by the receiving component.
State is mutable data managed within a component. It represents information that can change over time, usually in response to user actions. State is local to the component and can be updated using setState (class) or useState (function), causing the component to re-render.
| Props | State |
|---|---|
| Props are read-only. | State changes can be asynchronous. |
| Props are immutable. | State is mutable. |
| Props allow you to pass data from one component to other components as an argument. | State holds information about the components. |
| Props can be accessed by the child component. | State cannot be accessed by child components. |
| Props are used to communicate between components. | States can be used for rendering dynamic changes with the component. |
| Stateless component can have Props. | Stateless components cannot have State. |
| Props make components reusable. | State cannot make components reusable. |
| Props are external and controlled by whatever renders the component. | The State is internal and controlled by the React Component itself. |
Q. How to set state with a dynamic key name?
To set a state with a dynamic key name in React, use ES6 computed property names by wrapping the variable or expression in square brackets [] within your state update.
1. Using the useState() Hook:
When using objects in functional components, you must spread the previous state to avoid overwriting other keys
Example:
const [formData, setFormData] = useState({ firstName: '', lastName: '' });
const handleChange = (e) => {
const { name, value } = e.target;
// Use [name] to dynamically target the correct key
setFormData(prevState => ({
...prevState,
[name]: value
}));
};
2. Using Class Components (setState):
In class components, React automatically merges top-level state, so you don't always need to spread the previous state unless you are updating nested objects.
Example:
handleChange(event) {
const { name, value } = event.target;
// Square brackets evaluate the 'name' variable as the key
this.setState({
[name]: value
});
}
⚝ Try this example on CodeSandbox
Q. How to listen state change in React.js?
In class components, use the componentDidUpdate(prevProps, prevState) lifecycle method. It runs after every update, so you can compare prevState with the current state to detect changes.
Example:
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
console.log('Count changed:', this.state.count);
}
}
In functional components, use the useEffect() hook with the state variable as a dependency. The effect runs whenever that state changes.
Example:
export function MyComponent(props) {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Count changed:', count)
},[count]) // <-- here put the parameter to listen
}
Q. How to access child's state in React?
In React, you can access a child component's state from the parent using refs. This is mainly possible with class components. The parent creates a ref and attaches it to the child. The parent can then call methods on the child to get or update its state.
Example:
// Parent Component
class Parent extends React.Component {
constructor(props) {
super(props);
this.childRef = React.createRef();
}
handleClick = () => {
// Access child method or state
const childInstance = this.childRef.current;
alert(childInstance.state.value); // or call a method
};
render() {
return (
<>
<Child ref={this.childRef} />
<button onClick={this.handleClick}>Get Child State</button>
</>
);
}
}
// Child Component
class Child extends React.Component {
state = { value: "Hello from Child" };
render() {
return <div>{this.state.value}</div>;
}
}
Note:
This approach is not recommended for most cases. Prefer passing data up via callbacks. Use refs only when necessary.
⚝ Try this example on CodeSandbox
Q. How to change the state of a child component from its parent in React?
To change the state of a child component from its parent in React, you should follow the “lifting state up” pattern:
- Move the state you want to control into the parent component.
- Pass the state and a function to update it as props to the child component.
- The child component uses the function prop to request state changes.
/**
* Change Child state from its Parent
* @param {*} param0
*/
const Child = ({ open }) => {
return <h2>Child State: {open.toString()}</h2>;
};
const Parent = () => {
const [isOpen, setIsOpen] = React.useState(false);
const toggleChild = () => {
setIsOpen((prevValue) => !prevValue);
};
return (
<div>
<button onClick={toggleChild}>Click Me</button>
{/* Pass a callback to Child */}
<Child open={isOpen} />
</div>
);
};
export default Parent;
⚝ Try this example on CodeSandbox
Q. How does the state differ from props in React?
1. State:
This is data maintained inside a component. It is local or owned by that specific component. The component itself will update the state using the setState() function.
Example:
class AppComponent extends React.component {
state = {
msg : 'Hello World!'
}
render() {
return <div>Message {this.state.msg}</div>
}
}
2. Props:
Data passed in from a parent component. props are read-only in the child component that receives them. However, callback functions can also be passed, which can be executed inside the child to initiate an update.
Example: The parent can pass a props by using this
<ChildComponent color='red' />
Inside the ChildComponent constructor we could access the props
class ChildComponent extends React.Component {
constructor(props) {
super(props)
console.log(props.color)
}
}
Props can be used to set the internal state based on a prop value in the constructor, like this
class ChildComponent extends React.Component {
constructor(props) {
super(props)
this.state.colorName = props.color
}
}
Props should never be changed in a child component. Props are also used to allow child components to access methods defined in the parent component. This is a good way to centralize managing the state in the parent component, and avoid children to have the need to have their own state.
Difference between State and Props:
| Props | State |
|---|---|
| Props are read-only. | State changes can be asynchronous. |
| Props allow to pass data from one component to other components as an argument. | State holds information about the components. |
| Props can be accessed by the child component. | State cannot be accessed by child components. |
| Props are used to communicate between components. | States can be used for rendering dynamic changes with the component. |
| Stateless component can have Props. | Stateless components cannot have State. |
| Props are external and controlled by whatever renders the component. | The State is internal and controlled by the React Component itself. |
# 7. REACT HOOKS
Q. What are React Hooks?
React Hooks are in-built functions that let you “hook into” React features like state, lifecycle behavior, context, and more from function components (without writing class components).
Rules of Hooks:
- Call Hooks only at the top level
- Hooks should not be called inside loops, conditions, or nested functions.
- Hooks should be used inside React function components or custom Hooks
Built-in Hooks:
| Hooks | Description |
|---|---|
| useState() | To manage states. Returns a stateful value and an updater function to update it. |
| useEffect() | To manage side-effects like API calls, subscriptions, timers, mutations, and more. |
| useContext() | To return the current value for a context. |
| useReducer() | A useState alternative to help with complex state management. |
| useCallback() | It returns a memorized version of a callback to help a child component not re-render unnecessarily. |
| useMemo() | It returns a memoized value that helps in performance optimizations. |
| useRef() | It returns a ref object with a .current property. The ref object is mutable. It is mainly used to access a child component imperatively. |
| useImperativeHandle() | It customizes the instance value that is exposed to parent components when using ref. |
| useLayoutEffect() | It fires at the end of all DOM mutations. It's best to use useEffect as much as possible over this one as the useLayoutEffect fires synchronously. |
| useDebugValue() | Helps to display a label in React DevTools for custom hooks. |
Example:
/**
* useState() Hooks
*/
import { useState } from "react";
function App() {
const [isButtonClicked, setIsButtonClicked] = useState(false);
return (
<button onClick={() => setIsButtonClicked(!isButtonClicked)}>
{isButtonClicked ? "Clicked" : "Click Me, Please"}
</button>
);
}
export default App;
⚝ Try this example on CodeSandbox
Q. What are advantages of using React Hooks?
-
Code Reusability: Hooks allow you to extract stateful logic into custom hooks, which can then be shared across multiple components without changing the component hierarchy.
-
Improved Readability and Simplicity: Functional components with hooks generally have a flatter structure and are more concise than class components. They eliminate the need for ES6 classes, this keyword binding, and complex constructors.
-
Organized Logic: In class components, related logic is often split across different lifecycle methods (e.g., fetching data in componentDidMount and cleaning up in componentWillUnmount). Hooks like useEffect allow you to group related code together in one place.
-
Reduced “Wrapper Hell”: Hooks replace patterns like Higher-Order Components (HOCs) and render props, which often lead to deeply nested component trees that are difficult to debug and maintain.
-
Easier Testing: Because hooks are just JavaScript functions, they are easier to isolate and unit test compared to class components with complex lifecycle.
-
Performance Optimization:
- Hooks like
useMemo()anduseCallback()help prevent unnecessary re-calculations and re-renders by memoizing values and functions. - Functional components are generally lighter and easier for build tools to minify than class-based counterparts.
- Hooks like
Q. How to create custom Hooks?
React also allows us to create custom Hooks with unique features that extracts component logic into reusable functions.
A custom Hooks has following features:
- As a function, it takes input and returns output.
- Its name starts with use like useQuery, useMedia…
- Unlike functional components, custom hooks return a normal, non-jsx data.
- Unlike normal functions, custom hooks can use other hooks such as useState, useRef… and other custom hooks.
Example: Custom Hook - useFetch()
/**
* Custom Hook
*/
import { useState, useEffect } from "react";
const useFetch = (url: string) => {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => setData(data));
}, [url]);
return [data];
};
export default useFetch;
/**
* App Component
*/
import "./styles.css";
import useFetch from "./useFetch";
export default function App() {
// custom hook
const [data] = useFetch("https://jsonplaceholder.typicode.com/todos");
return (
<>
{data &&
data.map((item) => {
return <p key={item.id}>{item.title}</p>;
})}
</>
);
}
⚝ Try this example on CodeSandbox
Q. How to fetch data with React Hooks?
The two main hooks used for fetching data are:
useState()- Maintains local state in function componentsuseEffect()- Executes functions after a component renders (performs side effects)
Example:
/**
* useState() and useEffect() Hooks
*/
import { useState, useEffect } from "react";
interface User {
id: number;
login: string;
}
function App() {
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
fetch("https://api.github.com/users")
.then((response) => response.json())
.then((data) => setUsers(data));
}, []); // Empty dependency array = run once on mount
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.login}</li>
))}
</ul>
);
}
export default App;
⚝ Try this example on CodeSandbox
Q. Do Hooks replace render props and higher-order components?
In most cases, yes, Hooks replace the need for render props and higher-order components (HOCs). While they haven't made these patterns obsolete, Hooks are now the recommended default for sharing stateful logic in modern React applications.
Why Hooks are Preferred
-
Reduced Nesting: HOCs and render props often lead to “wrapper hell,” where components are buried under layers of wrappers. Hooks allow you to call logic directly inside a component without adding extra layers to the tree.
-
Clearer Data Flow: With HOCs, it can be unclear which prop comes from which wrapper (prop collision). Hooks make dependencies explicit because you call the hook and assign its return value to a variable you name yourself.
-
Better Composability: You can easily use multiple hooks together in a single component, whereas nesting multiple HOCs or render props quickly becomes difficult to read and maintain.
When You Might Still Use HOCs or Render Props
Despite the shift to Hooks, these patterns still have specific use cases:
Higher-Order Components:
-
Conditional Rendering: Useful for wrapping a component with logic that decides whether to render it at all (e.g., an withAuthentication wrapper).
-
External Components: Best when you need to inject behavior into a component you don't “own” or cannot modify.
-
Render Props:
-
Custom Renderers: Ideal when a component handles complex logic but needs the parent to define the actual UI (e.g., a virtual scroller where the parent provides a renderItem function).
-
Legacy Support: Necessary if you are maintaining older codebases that still use Class components, as Hooks only work in Functional components
-
Q. How to compare oldValues and newValues on React Hooks useEffect?
Use a ref to store the previous value. Refs persist across renders and updating them does not trigger a re-render, which makes them perfect for old vs new comparisons in useEffect.
Example:
// Custom Hook
const usePrevious = (value) => {
const ref = useRef();
// Store the current value in the ref AFTER every render
useEffect(() => {
ref.current = value;
}, [value]); // Only update when the value actually changes
// Return the value from the PREVIOUS render cycle
return ref.current;
};
import { useEffect, useRef, useState } from "react";
function Example() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
const prevCount = prevCountRef.current;
if (prevCount !== undefined && prevCount !== count) {
console.log("old:", prevCount, "new:", count);
}
// update ref after comparison
prevCountRef.current = count;
}, [count]);
return <button onClick={() => setCount((c) => c + 1)}>+1</button>;
}
We create a custom usePrevious hook that accepts the current state value as input and returns the value from the previous render. Inside the hook, useRef stores that value without causing a re-render, and useEffect updates ref.current after each render so it is available as the previous value on the next render.
⚝ Try this example on CodeSandbox
Q. How to re-render the view when the browser is resized?
To re-render a React component when the browser is resized, you can use the useEffect hook to listen to the resize event. When this event fires, you update the component's state, which automatically triggers a re-render.
Example:
import { useState, useEffect } from 'react';
/**
* Custom hook that tracks the current browser window dimensions.
* Updates on every resize event and cleans up the listener on unmount.
* @returns The current `{ width, height }` of the window, or `undefined` before first mount.
*/
function useWindowSize() {
const [windowSize, setWindowSize] = useState<{ width: number; height: number } | undefined>(
undefined
);
useEffect(() => {
const handleResize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowSize;
}
/**
* Page component that displays the current window width and height.
*/
export default function Page() {
const size = useWindowSize();
return (
<div>
<p>Width: {size?.width}</p>
<p>Height: {size?.height}</p>
</div>
);
}
Q. How can I force a component to re-render with hooks in React?
The useState() or useReducer() hooks can be used to force a React component to rerender.
- The
useState()hook returns an array with two elements, a value and an updater function. - Here, we are instantly calling the updater function, which in this case is called with
undefined, so it is the same as callingupdater(undefined).
Example:
/**
* Rerender Component using useState()
*/
import { useState } from "react";
function Example() {
const [, setTick] = useState(0);
const forceRender = () => {
setTick((tick) => tick + 1);
};
return <button onClick={forceRender}>Re-render</button>;
}
/**
* Rerender Component using useReducer()
*/
import { useReducer } from "react";
function Example() {
const [, forceRender] = useReducer((count) => count + 1, 0);
return <button onClick={forceRender}>Re-render</button>;
}
⚝ Try this example on CodeSandbox
Q. What is useState() in React?
The useState() is a built-in React Hook that allows to have state variables in functional components. State is data that needs to be tracked within an application, and when a state variable changes, React automatically re-renders the component to display the updated value.
Example:
import { useState } from 'react';
function Counter() {
// Declares a state variable 'count' initialized to 0, and a function 'setCount' to update it
const [count, setCount] = useState(0);
return (
<>
<p>You clicked {count} times</p>
{/* The onClick handler calls setCount to update the state, triggering a re-render */}
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</>
);
}
The useState() function takes as argument a value for the initial state. In this case, the count starts out with 0. In addition, the hook returns an array of two values: count and setCount.
Q. Why do we use array destructuring in useState?
We use array destructuring because useState returns a pair (an array with exactly two items), and destructuring is the cleanest way to give those items custom names in a single line.
The useState hook allows us to make our function components stateful. When called, useState() returns an array of two items. The first being our state value and the second being a function for setting or updating that value.
Example: State with Various Data Types
const [count, setCount] = useState(0)
const [color, setColor] = useState('#526b2d')
const [isHidden, setIsHidden] = useState(true)
const [products, setProducts] = useState([])
const [user, setUser] = useState({
name: '',
age: '',
email: '',
})
Q. What is useReducer() in React?
useReducer() is a React Hook for managing state with a reducer function, especially when state logic is complex or depends on previous state.
It accepts a reducer function with the application initial state, returns the current application state, then dispatches a function.
Syntax:
The useReducer Hook returns an array with exactly two values: the current state and a dispatch function
const [state, dispatch] = useReducer(reducer, initialState);
- reducer: A function that takes the current state and an action as arguments and returns the next state.
- initialState: The value the state starts with.
- dispatch: A function you call to trigger a state update by passing it an “action” (usually an object like { type: ‘increment’ }).
Example:
import { useReducer } from 'react'
type Action = 'increment' | 'decrement' | 'reset'
const initialState = 0
/**
* Reducer function for managing a counter state.
* @param state - The current counter value.
* @param action - The action to perform: 'increment', 'decrement', or 'reset'.
* @returns The updated counter value.
*/
const reducer = (state: number, action: Action) => {
switch (action) {
case 'increment': return state + 1
case 'decrement': return state - 1
case 'reset': return 0
default: throw new Error('Unexpected Action!')
}
}
/**
* Component that demonstrates useReducer with increment, decrement, and reset actions.
*/
const ReducerExample = () => {
const [count, dispatch] = useReducer(reducer, initialState)
return (
<div>
{count}
<button onClick={() => dispatch('increment')}>+1</button>
<button onClick={() => dispatch('decrement')}>-1</button>
<button onClick={() => dispatch('reset')}>reset</button>
</div>
)
}
export default ReducerExample
Here, we first define an initialState and a reducer. When a user clicks a button, it will dispatch an action which updates the count and the updated count will be displayed. We could define as many actions as possible in the reducer, but the limitation of this pattern is that actions are finite.
Q. When to use useReducer vs useState Hooks in React?
Use useState when:
-
Simple Primitives: Ideal for managing single strings, booleans, or numbers (e.g., a “show/hide” toggle or a simple counter).
-
Independent Updates: When changing one state value has no effect on others.
-
Straightforward Transitions: Updates that don't depend heavily on the previous state or don't require complex “if/else” validation before setting the new value
Use useReducer when:
-
Complex State Objects: When you have an object or array with many properties that often update together (e.g., a multi-field user registration form).
-
Interdependent Logic: When the “next” state depends on multiple parts of the “previous” state (e.g., a chess game board where moving a piece affects turn order and board layout).
-
Predictable Transitions: When you want to enforce specific “rules” for how state can change using a centralized reducer function.
-
Performance with Deep Updates: useReducer can be more efficient for passing state down to deep child components because the dispatch function remains stable (it doesn't change on re-renders), helping to avoid unnecessary updates in children.
-
Testing: Reducer functions are “pure” functions (meaning they only depend on their arguments), which makes them very easy to test in isolation from the UI.
Q. How does React renderer work exactly when we call setState?
When you call setState, the React renderer doesn't immediately touch the real DOM. Instead, it triggers a two-phase process managed by the Fiber reconciler to efficiently determine and apply only the necessary changes
1. The Render Phase (Asynchronous & Interruptible)
This phase is purely internal and does not produce user-visible changes.
-
Scheduling: React enqueues the state update in an internal updateQueue for that component. It then schedules a “unit of work” with a specific priority level.
-
Virtual DOM Creation: React calls the component's render method (or executes the function component) to generate a new Virtual DOM tree.
-
Diffing (Reconciliation): React compares the new Virtual DOM tree against the previous one using a heuristic algorithm.
-
Fiber Tree Construction: During this traversal, React builds a workInProgress fiber tree. Each fiber node represents a component and tracks “effects” (e.g., “this node needs an update” or “this node should be deleted”).
-
Time-Slicing: Because this phase has no side effects, React can pause it to let the browser handle high-priority tasks (like user input) and then resume exactly where it left off.
2. The Commit Phase (Synchronous & Non-Interruptible)
Once the entire tree has been processed and the “effect list” is complete, React enters the commit phase.
-
DOM Updates: The renderer (e.g., react-dom) iterates through the list of effects and applies the minimal set of changes to the Real DOM.
-
Switching Trees: After the DOM is updated, React points its “current” internal pointer to the workInProgress tree, making it the new source of truth for the next update.
-
Lifecycle & Effects: Finally, React executes post-mutation logic like
componentDidUpdateoruseEffecthooks.
Key Performance Features
-
Automatic Batching: React groups multiple setState calls within the same event loop into a single re-render to avoid unnecessary work.
-
Bailing Out: If you provide the same state value (based on a shallow comparison), React can “bail out” and skip the render phase for that entire subtree.
Q. What is a Webhook in React?
Webhooks provide a mechanism where by a server-side application can notify a client-side application when a new event (that the client-side application might be interested in) has occurred on the server.
These are HTTP-based callbacks that allow a backend server to “push” real-time data to another system when a specific event occurs (like a successful payment or a new GitHub commit).
How Webhooks Work with React
Because webhooks require a permanent URL (endpoint) to receive incoming POST requests, they cannot be hosted directly inside a React frontend. A standard integration works as follows:
- Backend Listener: You set up a backend server (e.g., Node.js/Express) with a public URL to receive the webhook.
- External Trigger: A third-party service (like Stripe or Shopify) sends a request to your backend URL when an event happens.
- Real-Time Update: Your backend then notifies your React app—typically using WebSockets (e.g., Socket.io) or a real-time database—to update the UI without the user refreshing the page.
Common Use Cases in React Apps
- Payments: Updating an order status to “Paid” after a Stripe webhook confirms the transaction.
- Authentication: Handling “Email Verified” events from services like Auth0.
- CI/CD: Triggering a new site build on Vercel when you push code to GitHub.
Q. Explain is useCallback(), useMemo(), useImperativeHandle(), useLayoutEffect(), useDebugValue() in React?
1. useCallback():
React's useCallback() Hook can be used to optimize the rendering behavior of your React function components. The useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.
This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).
Example:
/**
* Demonstrates useCallback() to memoize a function reference.
* The memoized `increment` callback is only recreated when `counter` changes, preventing unnecessary re-renders and function calls.
*/
import { useState, useCallback, useEffect } from "react";
const count = new Set();
function App() {
const [counter, setCounter] = useState(0);
const increment = useCallback(() => {
setCounter(counter + 1);
}, [counter]);
useEffect(() => {
count.add(increment);
console.log(count.size);
}, [increment]);
return (
<>
<h1>useCallback Hook Example</h1>
<h2>Function Call: {count.size}</h2>
<button onClick={increment}>Increment</button>
</>
);
}
export default App;
⚝ Try this example on CodeSandbox
2. useMemo():
The useMemo() is similar to useCallback() except it allows you to apply memoization to any value type. It does this by accepting a function which returns the value and then that function is only called when the value needs to be retrieved.
Example:
import React from 'react'
const users = [
{ id: '101', name: 'Dennis' },
{ id: '102', name: 'Alice' },
{ id: '103', name: 'Bob' },
{ id: '104', name: 'Carol' },
{ id: '105', name: 'Dave' }
]
/**
* App component that renders a searchable list of users.
* Uses `useMemo` to memoize the filtered user list, recomputing
* only when the search query changes.
*/
const App = () => {
const [search, setSearch] = React.useState('')
const filteredUsers = React.useMemo(
() => users.filter((user) => user.name.toLowerCase().includes(search.toLowerCase())),
[search]
)
return (
<div>
<input
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search users"
/>
<ul>
{filteredUsers.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
)
}
export default App
3. useImperativeHandle():
useImperativeHandle() customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with forwardRef.
import { useRef, useImperativeHandle, forwardRef } from 'react'
interface InputHandle {
focus: () => void
clear: () => void
}
/**
* A custom input component that exposes `focus` and `clear` methods
* to parent components via `useImperativeHandle`.
*/
const CustomInput = forwardRef<InputHandle>((_, ref) => {
const inputRef = useRef<HTMLInputElement>(null)
useImperativeHandle(ref, () => ({
focus() {
inputRef.current?.focus()
},
clear() {
if (inputRef.current) inputRef.current.value = ''
},
}))
return <input ref={inputRef} type="text" placeholder="Type something..." />
})
CustomInput.displayName = 'CustomInput'
/**
* App component that uses a ref to imperatively call `focus` and `clear`
* on the CustomInput child component.
*/
const App = () => {
const inputRef = useRef<InputHandle>(null)
return (
<div>
<CustomInput ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>Focus</button>
<button onClick={() => inputRef.current?.clear()}>Clear</button>
</div>
)
}
export default App
4. useLayoutEffect():
This runs synchronously immediately after React has performed all DOM mutations. This can be useful if you need to make DOM measurements (like getting the scroll position or other styles for an element) and then make DOM mutations or trigger a synchronous re-render by updating state.
As far as scheduling, this works the same way as componentDidMount and componentDidUpdate. Your code runs immediately after the DOM has been updated, but before the browser has had a chance to “paint” those changes (the user doesn't actually see the updates until after the browser has repainted).
Example:
import { useRef, useLayoutEffect } from 'react'
/**
* Demonstrates useLayoutEffect() to measure a DOM element\'s width
* synchronously after the browser has performed layout, before paint.
*/
const App = () => {
const boxRef = useRef<HTMLDivElement>(null)
const labelRef = useRef<HTMLSpanElement>(null)
useLayoutEffect(() => {
const measure = () => {
if (boxRef.current && labelRef.current) {
const width = boxRef.current.getBoundingClientRect().width
labelRef.current.textContent = `Box width: ${Math.round(width)}px`
}
}
measure()
window.addEventListener('resize', measure)
return () => window.removeEventListener('resize', measure)
}, [])
return (
<div>
<div ref={boxRef} style=>
Resize the window
</div>
<span ref={labelRef} />
</div>
)
}
export default App
useLayoutEffect vs useEffect:
- useLayoutEffect: If you need to mutate the DOM and/or do need to perform measurements
- useEffect: If you don't need to interact with the DOM at all or your DOM changes are unobservable (seriously, most of the time you should use this).
5. useDebugValue():
useDebugValue() can be used to display a label for custom hooks in React DevTools.
Example:
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null)
// ...
// Show a label in DevTools next to this Hook
useDebugValue(isOnline ? 'Online' : 'Offline')
return isOnline
}
Note::
- useCallback = memoize function
- useMemo = memoize value
- useImperativeHandle = expose controlled ref API
- useLayoutEffect = run before paint for layout work
- useDebugValue = improve custom hook visibility in DevTools
Q. What is the difference between useCallback() and useMemo()?
Both useCallback and useMemo are memoization hooks that prevent unnecessary recalculations on every render, but they memoize different things.
useCallback(fn, deps) — memoizes a function reference. Returns the same function object as long as the dependencies haven't changed. Use it to prevent child components from re-rendering unnecessarily when they receive a function as a prop.
useMemo(fn, deps) — memoizes a computed value. Calls the function and caches its return value. Use it to avoid expensive calculations being repeated on every render.
import { useState, useCallback, useMemo } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
const [items] = useState(['apple', 'banana', 'cherry', 'date']);
// useCallback — memoizes the function itself
// The same function reference is returned until 'count' changes
const handleClick = useCallback(() => {
console.log('Clicked, count is:', count);
}, [count]);
// useMemo — memoizes the return value of the function
// Only re-runs the filter when 'items' or 'count' changes
const filteredItems = useMemo(
() => items.filter(item => item.length > count),
[items, count]
);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
<ChildButton onClick={handleClick} /> {/* Won\'t re-render unless count changes */}
<ul>{filteredItems.map(i => <li key={i}>{i}</li>)}</ul>
</div>
);
}
const ChildButton = React.memo(({ onClick }) => {
console.log('ChildButton rendered');
return <button onClick={onClick}>Click me</button>;
});
Key Differences:
useCallback |
useMemo |
|
|---|---|---|
| Returns | The memoized function | The memoized value (result of calling the function) |
| Purpose | Stable function reference for props/deps | Cache expensive computation |
| Example use | Event handlers, callbacks passed to React.memo components |
Filtered lists, derived data, sorted arrays |
| Mental model | useCallback(fn, deps) ≡ useMemo(() => fn, deps) |
Calls fn, caches result |
When NOT to use them:
- Don't use either hook for cheap computations — the memoization overhead can cost more than the saved work.
- Don't use
useCallbackif the child component isn't wrapped inReact.memo— the memoized callback won't prevent re-renders. - Premature optimization is a code smell. Profile first with React DevTools Profiler, then optimize.
// ❌ Pointless — this is not an expensive operation
const double = useMemo(() => count * 2, [count]);
// ✅ Worthwhile — filtering a large array
const filtered = useMemo(() => largeDataset.filter(applyComplexFilters), [largeDataset, filters]);
Q. What is the difference between useEffect() and useLayoutEffect() hooks?
The main difference between the useEffect() hook and the useLayoutEffect() hook is that the useEffect() hook serves asynchronously, whereas the useLayoutEffect() hook works synchronously.
After all DOM mutations have been completed by React, useLayoutEffect executes synchronously. If you need to measure the DOM (for example, to determine the scroll position or other styles for an element) and then modify the DOM or cause a synchronous re-render by changing the state, this can be helpful.
1. useEffect():
- Runs after the browser paints the updated screen.
- Does not block visual updates.
- Best for non-visual side effects: API calls, subscriptions, timers, logging, updating document title.
2. useLayoutEffect():
- Runs synchronously after DOM updates but before the browser paints.
- Blocks painting until it finishes.
- Best when you must read layout or make DOM measurements/mutations before user sees anything: measuring element size/position, preventing flicker in animations or tooltip positioning.
Q. How to Memoize Components in React?
If your component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.
React.memo only checks for prop changes. If your function component wrapped in React.memo has a useState, useReducer or useContext Hook in its implementation, it will still rerender when state or context change.
Example: Let's take an example of search functionality. In the example below, the App component contains:
- Search input for the fruit name
- A button and a child component where the user search will be displayed
- A count of the number of times a user has clicked the button
import { useState, useMemo } from 'react';
const fruits = ['apple', 'orange', 'banana'];
/**
* App component that renders a searchable fruit list.
* Uses `useMemo` to memoize the filtered results, recomputing
* only when the search query changes.
*/
function App() {
const [query, setQuery] = useState('');
const filteredFruits = useMemo(
() => fruits.filter((fruit) => fruit.includes(query.toLowerCase())),
[query]
);
return (
<div>
<input
type="text"
placeholder="Search fruits..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<ul>
{filteredFruits.map((fruit) => (
<li key={fruit}>{fruit}</li>
))}
</ul>
</div>
);
}
export default App;
⚝ Try this example on CodeSandbox
Q. How to prevent unnecessary updates using React.memo()?
The React.memo() is a higher-order component that will memoize your component, very similar to PureComponent. It will shallowly compare current and new props of the component, and if nothing changes, React will skip the rendering of that component.
Key rules to prevent unnecessary updates:
- Wrap child component with React.memo.
- Pass stable props.
- Use useCallback for function props.
- Avoid creating new objects/arrays inline in JSX on every render.
- Use stable keys for list items.
Example:
// Memo.js
const Text = (props) => {
console.log(`Text Component`);
return <div>Text Component re-render: {props.count} times </div>;
};
const MemoText = React.memo(
(props) => {
console.log(`MemoText Component`);
return <div>MemoText Component re-render: {props.count} times </div>;
},
(preprops, nextprops) => true
);
// App.js
const App = () => {
console.log(`App Component`);
const [count, setCount] = useState(0);
return (
<>
<h2>This is function component re-render: {count} times </h2>
<Text count={count} />
<MemoText count={count} />
<br />
<button
onClick={() => {
setCount(count + 1);
}}
>
CLICK ME
</button>
</>
);
};
⚝ Try this example on CodeSandbox
Q. What is useContext() hook in React?
useContext() is a built-in React Hook that lets you read and subscribe to a React Context without wrapping your component in a Context.Consumer.
Syntax:
const value = useContext(SomeContext);
It takes a Context object (created by React.createContext()) and returns the current context value provided by the nearest matching <Context.Provider> above the component in the tree.
Example:
import { createContext, useContext, useState } from "react";
// 1. Create context
const ThemeContext = createContext("light");
// 2. Consumer component
function ThemedButton() {
const theme = useContext(ThemeContext); // reads the nearest Provider value
return (
<button style=>
Current theme: {theme}
</button>
);
}
// 3. Provider component
export default function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={theme}>
<ThemedButton />
<button onClick={() => setTheme(t => t === "light" ? "dark" : "light")}>
Toggle Theme
</button>
</ThemeContext.Provider>
);
}
Key Points:
- The component calling
useContext()always re-renders when the context value changes. useContext()replaces the need forContext.Consumerwrapper orstatic contextTypein class components.- For performance, consider memoizing the context value with
useMemoto avoid unnecessary re-renders. - Pass the context object itself (from
createContext), not the Provider or Consumer.
Q. What is useTransition() in React?
useTransition() is a React Hook (introduced in React 18) that lets you mark a state update as non-urgent, keeping the UI responsive during expensive re-renders. React will process urgent updates (like typing) first, then apply the transition update when the browser is idle.
Syntax:
const [isPending, startTransition] = useTransition();
isPending—truewhile the transition is still processing; use it to show a loading/spinner indicator.startTransition(fn)— wrap your non-urgent state update inside this function.
Example: Filtering a large list without blocking the input
import { useState, useTransition } from "react";
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`);
export default function App() {
const [query, setQuery] = useState("");
const [filtered, setFiltered] = useState(items);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value); // urgent: update the input immediately
startTransition(() => {
// non-urgent: heavy filtering deferred until after input updates
setFiltered(items.filter((item) => item.toLowerCase().includes(value.toLowerCase())));
});
};
return (
<div>
<input value={query} onChange={handleChange} placeholder="Search..." />
{isPending && <p>Updating list...</p>}
<ul>
{filtered.slice(0, 50).map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
When to use useTransition():
- Filtering or searching large data sets
- Navigating between tabs or views that involve expensive renders
- Any state update where a brief delay is acceptable to keep the UI interactive
⚝ Try this example on CodeSandbox
Q. What is useDeferredValue() in React?
useDeferredValue() is a React Hook (introduced in React 18) that lets you defer re-rendering a non-urgent part of the UI. You pass it a value, and React will keep showing the old value while it prepares the updated expensive render in the background.
It is similar to useTransition() but works on values rather than on the state-setter call — useful when you don't control the state update that triggers the expensive render (e.g., a value comes from a prop or external store).
Syntax:
const deferredValue = useDeferredValue(value);
Example: Deferring a slow list render while keeping the input snappy
import { useState, useDeferredValue, memo } from "react";
const SlowList = memo(({ query }) => {
// Simulate expensive render
const items = Array.from({ length: 5000 }, (_, i) => `Result ${i + 1} for "${query}"`);
return <ul>{items.slice(0, 20).map((item) => <li key={item}>{item}</li>)}</ul>;
});
export default function App() {
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query); // lags behind `query` during heavy renders
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
{/* Input stays responsive; SlowList re-renders are deferred */}
<SlowList query={deferredQuery} />
</div>
);
}
useDeferredValue vs useTransition:
| Feature | useTransition |
useDeferredValue |
|---|---|---|
| Controls | The state update call | A derived value |
| Use when | You own the state setter | You receive the value as a prop |
| API | startTransition(() => setState(...)) |
const deferred = useDeferredValue(value) |
isPending flag |
✅ Yes | ❌ No (use deferred !== value instead) |
Q. What are the Rules of Hooks in React?
The Rules of Hooks are two fundamental constraints enforced by React (and the eslint-plugin-react-hooks linter) that ensure hooks work correctly and predictably.
Rule 1: Only call Hooks at the top level
Never call Hooks inside loops, conditions, nested functions, or after early returns. Hooks must always be called in the same order on every render.
// ❌ WRONG — conditional Hook call
function Component({ isLoggedIn }) {
if (isLoggedIn) {
const [name, setName] = useState(''); // Breaks rule — different call order each render
}
}
// ✅ CORRECT — call at the top level, put condition inside
function Component({ isLoggedIn }) {
const [name, setName] = useState(''); // Always called
if (!isLoggedIn) return null; // Guard after hooks
}
Rule 2: Only call Hooks from React functions
Call Hooks only from React function components or custom Hooks — never from regular JavaScript functions, class components, or event handlers.
// ❌ WRONG — calling a Hook from a plain function
function fetchUser() {
const [user, setUser] = useState(null); // Not a React function
}
// ✅ CORRECT — inside a React function component
function UserProfile() {
const [user, setUser] = useState(null); // OK
}
// ✅ CORRECT — inside a custom Hook (starts with "use")
function useUser() {
const [user, setUser] = useState(null); // OK
}
Why do these rules exist?
React relies on the call order of Hooks to associate state with the correct component instance. On every render, React expects the same number of Hooks to be called in the same sequence. If you skip a Hook call (due to a condition), React loses track of which state corresponds to which Hook.
// Internally, React tracks hooks by index: [state[0], state[1], state[2], ...]
// If hook[1] is skipped on re-render, hook[2] state gets assigned to hook[1] → bugs
Enforcing the rules with ESLint:
npm install eslint-plugin-react-hooks --save-dev
// .eslintrc
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
Q. What are the common pitfalls of the useEffect dependency array?
The dependency array is the most misunderstood part of useEffect. Missing or incorrectly specified dependencies lead to subtle bugs including stale closures, infinite loops, and missed updates.
Pitfall 1: Missing dependencies (stale closure)
// ❌ WRONG — 'count' is missing from deps; the effect closes over the initial value
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
console.log(count); // Always logs 0 — stale closure!
setCount(count + 1); // count is always 0 inside here
}, 1000);
return () => clearInterval(interval);
}, []); // empty deps — runs once, never re-subscribes
}
// ✅ FIX — use functional update form to avoid needing 'count' in deps
useEffect(() => {
const interval = setInterval(() => {
setCount(prev => prev + 1); // ✅ No stale closure — uses latest value
}, 1000);
return () => clearInterval(interval);
}, []);
Pitfall 2: Infinite loop from object/function deps
// ❌ WRONG — new object created on every render → effect runs infinitely
function Search({ query }) {
const options = { url: '/api/search', params: { q: query } }; // new ref every render
useEffect(() => {
fetch(options.url); // re-runs forever
}, [options]);
// ✅ FIX 1 — use primitive values as deps
useEffect(() => {
fetch(`/api/search?q=${query}`);
}, [query]); // only runs when query changes
// ✅ FIX 2 — useMemo to stabilize the object
const options = useMemo(() => ({ url: '/api/search', params: { q: query } }), [query]);
}
Pitfall 3: Unnecessary dependencies causing extra re-runs
// ❌ Using a function defined in the component as a dep — it changes every render
function Component() {
const fetchData = () => fetch('/api/data'); // new function every render
useEffect(() => {
fetchData();
}, [fetchData]); // runs every render because fetchData is always new
// ✅ FIX — wrap the function in useCallback or move it inside useEffect
const fetchData = useCallback(() => fetch('/api/data'), []);
useEffect(() => {
fetchData();
}, [fetchData]); // stable reference — runs only once
}
Pitfall 4: Not cleaning up async effects (race condition)
// ❌ If 'id' changes fast, an earlier fetch may resolve after a later one
useEffect(() => {
fetch(`/api/user/${id}`)
.then(res => res.json())
.then(data => setUser(data)); // may set stale data
}, [id]);
// ✅ FIX — use AbortController or an "active" flag
useEffect(() => {
let active = true;
fetch(`/api/user/${id}`)
.then(res => res.json())
.then(data => {
if (active) setUser(data); // only update if effect is still current
});
return () => { active = false; };
}, [id]);
Quick reference:
| Issue | Cause | Fix |
|---|---|---|
| Stale value | Missing dep | Add dep or use functional updater |
| Infinite loop | Object/function dep | useMemo / useCallback or primitive dep |
| Race condition | Async + fast dep changes | AbortController or active flag |
| Extra runs | Unstable function dep | useCallback to memoize function |
Q. What are stale closures in React Hooks and how do you fix them?
A stale closure is a bug where a function “remembers” an old version of a variable from a previous render, even though the state has since been updated. It's one of the most common React Hook pitfalls.
How stale closures happen:
function Counter() {
const [count, setCount] = useState(0);
// This handler captures 'count = 0' at the time it was created
const handleClick = () => {
setTimeout(() => {
// 3 seconds later, 'count' is still 0 — even if user clicked many times
alert(`Count is: ${count}`); // ← Stale closure!
}, 3000);
};
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<button onClick={handleClick}>Show count in 3s</button>
</div>
);
}
Fix 1: Use a ref to always hold the latest value
function Counter() {
const [count, setCount] = useState(0);
const countRef = useRef(count);
// Keep ref in sync with state
useEffect(() => {
countRef.current = count;
}, [count]);
const handleClick = () => {
setTimeout(() => {
alert(`Count is: ${countRef.current}`); // ✅ Always fresh
}, 3000);
};
}
Fix 2: Use functional state updater (avoids capturing state)
// ❌ Stale — captures 'count' at time of call
setCount(count + 1);
// ✅ Fresh — React passes the latest state as argument
setCount(prevCount => prevCount + 1);
Fix 3: Move the value into useEffect dependencies
// ❌ Stale — handler is memoized with old 'count'
const handleSave = useCallback(() => {
saveData(count); // stale if count changed
}, []); // missing 'count' in deps
// ✅ Add count to deps — new function created when count changes
const handleSave = useCallback(() => {
saveData(count);
}, [count]);
Common places stale closures appear:
| Pattern | Risk | Fix |
|---|---|---|
setTimeout / setInterval |
High | useRef for latest value |
useEffect with [] |
High | Functional updater or add deps |
useCallback with missing deps |
High | Exhaust deps or useRef |
| Event listeners attached once | Medium | Re-register on change or use ref |
| Async functions in effects | High | Capture all deps or abort |
# 8. REACT EVENTS
Q. What is meant by event handling in React?
Event handling in React means responding to user actions like clicks, typing, form submits, mouse movement, etc., by attaching handler functions to JSX elements.
Core Concepts:
-
CamelCase Naming: Unlike HTML where events are lowercase (e.g., onclick), React uses camelCase for event names (e.g., onClick, onChange, onSubmit).
-
Function References: You pass a function itself as the event handler, rather than a string of code. For example, use
onClick={handleClick}instead ofonclick="handleClick()”. -
Synthetic Events: React wraps native browser events in a SyntheticEvent object. This acts as a “cross-browser wrapper” that ensures the event behaves identically regardless of which browser the user is using.
Example:
/**
* Event Handling
*/
function Button() {
const handleClick = () => {
console.log("Button clicked");
};
return <button onClick={handleClick}>Click Me</button>;
}
⚝ Try this example on CodeSandbox
Q. What are the common event handlers in React?
React provides a wide range of built-in event handlers that follow standard HTML events but use camelCase naming conventions. Below are the most common event handlers categorized by their interaction type:
1. Mouse Events
- onClick: Triggered when a user clicks on an element.
- onDoubleClick: Triggered when a user double-clicks an element.
- onMouseEnter / onMouseLeave: Fired when the mouse pointer enters or leaves the boundary of an element.
- onMouseDown / onMouseUp: Triggered when a mouse button is pressed or released.
- onMouseMove: Fires as the mouse moves over an element.
2. Form & Input Events
- onChange: Captures user input in fields like
<input>,<textarea>, and<select>. - onSubmit: Handles form submissions. It is standard practice to use e.preventDefault() within this handler to stop a full page refresh.
- onFocus / onBlur: Fired when an element (like an input) gains or loses focus.
- onInput: Fires whenever the value of an element is changed.
3. Keyboard Events
- onKeyDown: Triggered when a user first presses a key.
- onKeyUp: Triggered when a user releases a key.
- onKeyPress: Triggered while a key is being pressed down (now largely deprecated in favor of onKeyDown)
4. Other Common Events
- Touch Events: onTouchStart, onTouchMove, and onTouchEnd are used specifically for mobile interactions.
- Scroll Events: onScroll is triggered when an element's scroll position changes.
- Drag & Drop: React supports multiple drag events like onDragStart, onDragOver, and onDrop.
- Clipboard: onCopy, onCut, and onPaste handle user clipboard actions.
Q. How to pass a parameter to event handlers in React?
Example:
const message = "Hello World!";
export default function App() {
const displayMessage = (message) => {
alert(message);
};
return (
<button onClick={() => displayMessage(message)}>CLICK ME</button>
);
}
⚝ Try this example on CodeSandbox
Q. How do you pass an event handler to a component?
In React, you pass an event handler to a component by defining a function in the parent and passing it as a prop to the child component. The child then “plugs” that prop into a standard HTML event attribute like onClick.
Pattern:
- Define the handler in the parent
- Pass it as a prop to the child
- The child attaches it to a DOM element
Example:
/**
* Pass an event handler to a component
*/
import { useState } from "react";
// Child receives handler via props and wires it to a DOM event
const CustomButton = ({ onCustomClick }: { onCustomClick: () => void }) => {
return <button onClick={onCustomClick}>Increment</button>;
};
// Parent defines the handler and passes it down
const Container = () => {
const [counter, setCounter] = useState(0);
const handleCustomClick = () => {
setCounter(counter + 1);
};
return (
<div>
<p>Count: {counter}</p>
<CustomButton onCustomClick={handleCustomClick} />
</div>
);
};
export default Container;
⚝ Try this example on CodeSandbox
Q. What is the difference between HTML and React event handling?
In HTML, the attribute name is in all lowercase and is given a string invoking a function defined somewhere:
<!-- HTML -->
<button onclick="handleClick()">Click</button>
In React, the attribute name is camelCase and are passed the function reference inside curly braces:
// React
<button onClick={handleClick}>Click</button>
In HTML, false can be returned to prevent default behavior, whereas in React preventDefault() has to be called explicitly.
<a href="#" onclick="console.log('The link was clicked.'); return false" />
function handleClick(e) {
e.preventDefault()
console.log("The link was clicked.")
}
Q. How to bind methods or event handlers in JSX callbacks?
There are 3 possible ways to achieve this
1. Event Handler in Render Method:
We can bind the handler when it is called in the render method using bind() method.
handleClick() {
// ...
}
<button onClick={this.handleClick.bind(this)}>Click</button>
⚝ Try this example on CodeSandbox
2. Event Handler using Arrow Function:
In this approach we are binding the event handler implicitly. This approach is the best if you want to pass parameters to your event.
handleClick() {
// ...
}
<button onClick={() => this.handleClick()}>Click</button>
⚝ Try this example on CodeSandbox
3. Event Handler in Constructor:
This has performance benefits as the events aren't binding every time the method is called, as opposed to the previous two approaches.
constructor(props) {
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// ...
}
<button onClick={this.handleClick}>Click</button>
⚝ Try this example on CodeSandbox
Q. Why do we need to bind methods inside class component constructor?
In Class Components, when we pass the event handler function reference as a callback like this
<button type="button" onClick={this.handleClick}>Click Me</button>
the event handler method loses its implicitly bound context. When the event occurs and the handler is invoked, the this value falls back to default binding and is set to undefined, as class declarations and prototype methods run in strict mode.
When we bind the this of the event handler to the component instance in the constructor, we can pass it as a callback without worrying about it losing its context.
Arrow functions are exempt from this behavior because they use lexical this binding which automatically binds them to the scope they are defined in.
Example:
/**
* Event Handling in React
*/
import React from "react";
export default class App extends React.Component {
constructor(props: object) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert("Click event triggered!");
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
⚝ Try this example on CodeSandbox
Q. How do I pass a parameter to an event handler or callback?
You can use an arrow function to wrap around an event handler and pass parameters:
1. Arrow function (most common):
<button onClick={() => handleClick(id)}>Click</button>
2. bind version (equivalent):
This is equivalent to calling .bind
<button onClick={handleClick.bind(null, id)}>Click</button>
// using event object
<button onClick={(e) => handleClick(id, e)}>Click</button>
Example:
/**
* Pass parameter to an event handler
*/
import { useState } from 'react';
const A = 65; // ASCII character code
const App = () => {
const [justClicked, setJustClicked] = useState<string | null>(null);
const [letters] = useState(Array.from({ length: 26 }, (_, i) => String.fromCodePoint(A + i)));
const handleClick = (letter: string) => {
setJustClicked(letter);
};
return (
<>
Just clicked: {justClicked}
<ul>
{letters.map((letter) => (
<li key={letter}>
<button type="button" onClick={() => handleClick(letter)}>
{letter}
</button>
</li>
))}
</ul>
</>
);
};
export default App;
⚝ Try this example on CodeSandbox
Q. When should we use arrow functions with React?
In modern React, arrow functions are the standard for most use cases due to their concise syntax and lexical this binding, which simplifies handling state and props
When to Use Arrow Functions
-
Defining Functional Components: Arrow functions provide a lightweight and declarative way to define stateless components.
-
Simple Event Handlers: Use them for short, one-off logic directly in JSX, such as
<button onClick={() => setOpen(true)} />. -
Passing Arguments to Handlers: They are the easiest way to pass specific data to a function when an event occurs, e.g.,
onClick={() => handleDelete(id)}. -
Array Methods: They are ideal for iterating over data to generate lists (e.g.,
.map(),.filter()) because they reduce visual noise. -
Asynchronous Callbacks: Arrow functions preserve the context of the outer scope, making them reliable for
.then()blocks or setTimeout within hooks. -
Class Methods (Legacy): If you still use class components, defining methods as arrow functions (class properties) avoids the need to manually
.bind(this)in the constructor.
Example:
import React from 'react'
class Button extends React.Component {
render() {
return (
<button onClick={this.handleClick} style={this.state}>
Set background to red
</button>
)
}
handleClick = () => {
this.setState({ backgroundColor: 'red' })
}
}
- When we use
thisit generates a new function on every render, which will obviously have a new reference. - If the component we pass this generated function to is extending
PureComponent(), it will not be able to bail out on rerendering, even if the actual data has not changed.
Q. Is it good to use arrow functions in render methods?
Problem:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. When arrow functions and binds in render. It breaks performance optimizations like shouldComponentUpdate() and PureComponent.
Example:
class App extends React.Component {
constructor(props) {
super(props);
...
}
deleteUser = id => {
this.setState(prevState => {
return {
users: prevState.users.filter(user => user.id !== id)
};
});
};
render() {
return (
<div>
<ul>
{this.state.users.map(user => {
return (
<User key={user.id} name={user.name} id={user.id}
onDeleteClick={() => this.deleteUser(user.id)}
/>
);
})}
</ul>
</div>
);
}
}
⚝ Try this example on CodeSandbox
Solution:
In below example, App Component has no arrow function in render. Instead, the relevant data is passed down to User Component. In User Component, onDeleteClick() calls the onClick function passed in on props with the relevant user.id.
// User Component
class User extends React.PureComponent {
onDeleteClick = () => {
// No bind needed since we can compose the relevant data for this item here
this.props.onClick(this.props.user.id);
};
render() {
console.log(`${this.props.user.name} just rendered`);
return (
<li>
<input type="button" value="Delete" onClick={this.onDeleteClick} />
{this.props.user.name}
</li>
);
}
}
// App Component
class App extends React.Component {
constructor(props) {
super(props);
...
}
deleteUser = id => {
this.setState(prevState => {
return {
users: prevState.users.filter(user => user.id !== id)
};
});
};
renderUser = user => {
return <User key={user.id} user={user} onClick={this.deleteUser} />;
}
render() {
return (
<div>
<ul>
{this.state.users.map(this.renderUser)}
</ul>
</div>
);
}
}
⚝ Try this example on CodeSandbox
Q. How to avoid the need for binding in React?
1. Use Arrow Function in Class Property:
Arrow methods capture lexical this, so they are auto-bound.
import React from 'react';
class Button extends React.Component {
state = { clicked: false };
handleClick = () => {
this.setState({ clicked: true });
};
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}
export default Button;
2. Bind in Render:
onChange={this.handleChange.bind(this)}
This approach is terse and clear, however, there are performance implications since the function is reallocated on every render.
3. Bind in Constructor:
One way to avoid binding in render is to bind in the constructor
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
}
This is the approach currently recommended in the React docs for “better performance in your application”.
4. Function components with hooks (modern React):
import React from 'react';
function Button() {
const [clicked, setClicked] = React.useState(false);
const handleClick = () => setClicked(true);
return <button onClick={handleClick}>{clicked ? "Clicked!" : "Click"}</button>;
}
export default Button;
Q. How do I bind a function to a component instance?
There are several ways to make sure functions have access to component attributes like this.props and this.state, depending on which syntax and build steps you are using.
1. Bind in Constructor (ES5):
import { Component } from 'react';
class App extends Component {
constructor(props: object) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
console.log('Clicked!');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>
}
}
export default App;
2. Class Properties:
import { Component } from 'react';
class App extends Component {
handleClick = () => {
console.log('Clicked!');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>
}
}
export default App;
3. Bind in Render:
import { Component } from 'react';
class App extends Component {
handleClick() {
console.log('Click happened')
}
render() {
return <button onClick={this.handleClick.bind(this)}>Click Me</button>
}
}
export default App;
Note:
Using Function.prototype.bind in render creates a new function each time the component renders, which may have performance implications
4. Arrow Function in Render:
import { Component } from 'react';
class App extends Component {
handleClick() {
console.log('Click happened')
}
render() {
return <button onClick={() => this.handleClick()}>Click Me</button>
}
}
export default App;
Note:
Using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison.
Best Practice:
- Prefer function components today (no this binding needed).
- If using class components, prefer constructor binding or class field arrow methods.
- Avoid inline bind/arrow in render for hot paths because they create a new function every render.
Q. How can I prevent a function from being called too quickly?
1. Throttle:
Throttling prevents a function from being called more than once in a given window of time. It runs at most once every N ms. Good for scroll, resize, mousemove.
Example:
import { throttle } from "lodash";
const onScroll = throttle(() => {
console.log("run at most every 200ms");
}, 200);
2. Debounce:
Debouncing ensures that a function will not be executed until after a certain amount of time has passed since it was last called. This can be useful when you have to perform some expensive calculation in response to an event that might dispatch rapidly (eg scroll or keyboard events).
It waits until calls stop for N ms, then runs once. Good for search input, autosave.
Example:
import { debounce } from "lodash";
const onSearchChange = debounce((value) => {
console.log("run after user stops typing");
}, 300);
Example:
/**
* Throttle and Debounce in React
*/
import * as React from "react";
import * as _ from "lodash";
export default class App extends React.Component<object, { count: number }> {
state = { count: 0 };
handleCount() {
this.setState((state) => ({
count: state.count + 1
}));
}
// You will run count() only once after 100ms
handleDebounce = _.debounce(() => this.handleCount(), 100);
// You will run count() every 200ms
handleThrottle = _.throttle(() => this.handleCount(), 200);
render() {
return (
<div>
{this.state.count}
<hr />
<button onClick={this.handleThrottle}>Click Me - Throttle </button>
<button onClick={this.handleDebounce}>Click Me - Debounce </button>
</div>
);
}
}
⚝ Try this example on CodeSandbox
3. RequestAnimationFrame Throttling:
RequestAnimationFrame (rAF) Throttling is a performance optimization technique that limits the execution of a function to match the browser's native repaint cycle (typically 60 times per second, or every 16.67ms).
/**
* RequestAnimationFrame Throttling
*/
/**
* RequestAnimationFrame Throttling
*/
import React from "react";
import rafSchedule from "raf-schd";
type Point = { x: number; y: number };
type Props = { onScroll: (point: Point) => void };
export default class App extends React.Component<Props> {
scheduleUpdate = rafSchedule((point: Point) => this.props.onScroll(point));
constructor(props: Props) {
super(props);
this.handleScroll = this.handleScroll.bind(this);
}
handleScroll(e: React.UIEvent<HTMLDivElement> & { clientX: number; clientY: number }) {
// When we receive a scroll event, schedule an update.
// If we receive many updates within a frame, we'll only publish the latest value.
this.scheduleUpdate({ x: e.clientX, y: e.clientY });
}
componentWillUnmount() {
// Cancel any pending updates since we're unmounting.
this.scheduleUpdate.cancel();
}
render() {
return (
<div style= onScroll={this.handleScroll}>
<img src="https://picsum.photos/800/600" alt="Nature" />
</div>
);
}
}
⚝ Try this example on CodeSandbox
Q. Explain synthetic event in React.js?
A Synthetic Event in React is a cross-browser wrapper around the browser's native event system. It provides a consistent interface that ensures events behave identically across different browsers, such as Chrome, Firefox, and Safari, by normalizing their properties
Key Features:
-
Cross-Browser Consistency: React abstracts away browser-specific quirks, so you don't have to worry about inconsistencies in event names or behaviors.
-
Event Delegation: To improve performance, React doesn't attach event listeners to individual DOM nodes. Instead, it attaches a single event listener at the root of your application and uses bubbling to catch and manage events.
-
Standardized Interface: It implements the same interface as the W3C specification, including methods like
preventDefault()andstopPropagation(). -
Access to Native Events: If you need the raw browser event for specialized use cases, you can access it via the nativeEvent attribute.
Example:
function Button() {
const handleClick = (e) => {
e.preventDefault();
console.log(e.type); // "click" (Synthetic Event)
console.log(e.nativeEvent); // underlying DOM event
};
return <button onClick={handleClick}>Click</button>;
}
SyntheticEvent Object:
React SyntheticEvent gives a normalized event object with these common methods/properties:
void preventDefault()
void stopPropagation()
boolean isPropagationStopped()
boolean isDefaultPrevented()
boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
DOMEventTarget target
number timeStamp
string type
Q. How to trigger click event programmatically?
To trigger a click event programmatically in React, you typically use a ref to access the underlying DOM element and then call its native .click() method
Example:
import { useRef } from 'react';
function MyComponent() {
const buttonRef = useRef(null);
const triggerClick = () => {
// Check if the ref is attached, then trigger the click
buttonRef.current?.click();
};
return (
<>
{/* Target element */}
<button ref={buttonRef} onClick={() => console.log("Real button clicked")}>Target Button</button>
<button onClick={triggerClick}>Trigger Programmatically</button>
</>
);
}
Q. How to listen for click events that are outside of a component?
To listen for click events outside a React component, the standard approach is to attach a click listener to the document and use a Ref to check if the clicked element is contained within your component.
Using Custom Hook:
Example:
import { useEffect, useRef } from "react";
function OutsideClickBox({ onOutsideClick, children }) {
const boxRef = useRef(null);
useEffect(() => {
function handlePointerDown(event) {
if (!boxRef.current) return;
if (!boxRef.current.contains(event.target)) {
onOutsideClick?.(event);
}
}
document.addEventListener("mousedown", handlePointerDown);
return () => {
document.removeEventListener("mousedown", handlePointerDown);
};
}, [onOutsideClick]);
return <div ref={boxRef}>{children}</div>;
}
How it works:
- Attach a ref to the component root.
- Listen on document for
mousedownortouchstart. - If
event.targetis not insideref.current, treat it as an outside click. - Remove listeners in cleanup to avoid leaks.
Note:
Use mousedown instead of click if you want the outside action to happen earlier (before focus/click side effects inside other elements).
⚝ Try this example on CodeSandbox
Q. How to convert text to uppercase on user input entered?
To convert text to uppercase as a user types, you should update the state using .toUpperCase() within the onChange handler. This keeps your React state and the input field in sync.
Example:
import { useState } from "react";
export default function App() {
const [text, setText] = useState("");
const handleChange = (e) => {
// Convert the input value to uppercase before saving to state
setText(e.target.value.toUpperCase());
};
return (
<input
type="text"
value={text}
onChange={handleChange}
placeholder="TYPE SOMETHING..."
/>
);
}
Q. What are the pointer events in React?
In React, pointer events are a set of Synthetic Events designed to provide a unified way to handle inputs from various pointing devices, including mice, touchscreens, and pens/styluses. Instead of writing separate logic for mouse and touch interactions, you can use these events to cover all input types with a single handler.
Common pointer event handlers are:
- onPointerDown
- onPointerMove
- onPointerUp
- onPointerCancel
- onGotPointerCapture
- onLostPointerCapture
- onPointerEnter
- onPointerLeave
- onPointerOver
- onPointerOut
Example:
function Box() {
const handlePointerDown = (e) => {
console.log("Started with:", e.pointerType); // mouse | touch | pen
};
return (
<div
onPointerDown={handlePointerDown}
onPointerMove={(e) => console.log(e.clientX, e.clientY)}
onPointerUp={() => console.log("Pointer up")}
style=
>
Interact here
</div>
);
}
Q. What is an alternative way to avoid having to bind to this in event callback methods?
1. Bind in Constructor:
class App extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
console.log('Clicked !')
}
render() {
return <button onClick={this.handleClick}>Click Me</button>
}
}
2. Bind in Render:
class App extends Component {
handleClick() {
console.log('Clicked !')
}
render() {
return <button onClick={this.handleClick.bind(this)}>Click Me</button>
}
}
3. Arrow Function in Render:
class App extends Component {
handleClick() {
console.log('Clicked !')
}
render() {
return <button onClick={() => this.handleClick()}>Click Me</button>
}
}
Note:
Using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison.
Q. What is the alternative of binding this in the constructor?
Arrow Function creates and binds the function all at once. Inside render (and elsewhere), the function is already bound because the arrow function preserves the this binding.
Example:
class Button extends React.Component {
// no binding
handleClick = (e) => {
console.log('clicked !');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
# 9. REACT LISTS
Q. Explain the Lists in React?
In React, Lists refer to the practice of rendering multiple similar components or elements from a collection of data, such as an array. Lists are rendered by transforming an array of data into JSX elements, usually with .map().
Example:
/**
* React List
*/
const items = ['Apple', 'Banana', 'Cherry'];
function FruitList() {
return (
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
}
The “Key” Prop:
- React uses keys to track which items were added, removed, or reordered.
- Stable keys improve rendering performance and prevent UI bugs.
- Prefer stable IDs from data, not array index (especially if list order can change).
⚝ Try this example on CodeSandbox
Q. Why do I need Keys in React Lists?
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity:
Example:
const numbers = [10, 20, 30, 40, 50];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
React recommends that you do not use indexes as keys, if the order of items may change. It could impact performance negatively and could lead to some unstable component behavior.
Q. How to display an array of strings in react component?
To display an array of strings in a React component, the most common and recommended approach is to use the .map() method. This method iterates through the array and returns a new array of JSX elements that React can render.
Example:
function MyList() {
const items = ["this is line #1", "this is line #2", "this is line #3"];
return (
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
}
export default MyList;
⚝ Try this example on CodeSandbox
Q. How do you render Array, Strings and Numbers in React?
/**
* Array Component
*/
const frameworks = [
{ id: 1, name: "Angular"},
{ id: 2, name: "React"},
{ id: 3, name: "Vue.js"}
];
const ArrayList = (props) => (
<div>
<h2>Array of Objects</h2>
<ul>
{frameworks.map((item, index) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
/**
* String Component
*/
const words = ["hello", "react", "world"];
const StringList = (props) => (
<h3>Array of Strings</h3>
<ul>
{words.map((word, index) => (
<li key={index}>{word}</li>
))}
</ul>
);
/**
* Number Component
*/
const numbers = [10, 20, 30];
const NumberList = (props) => (
<h3>Array of Numbers</h3>
<ul>
{numbers.map((num) => (
<li key={num}>{num}</li>
))}
</ul>
);
⚝ Try this example on CodeSandbox
Q. How do keys help React reconciliation?
When React re-renders a list, it needs to figure out which items changed, were added, or were removed. Without keys, React compares elements by their position in the array. With keys, React compares elements by their stable identity, which is far more efficient and correct.
React's diffing algorithm (reconciliation) works in two phases:
-
Without keys (index-based diffing): React assumes the element at position 0 is the same element as before, position 1 is the same, and so on. If you prepend an item to a list of 100 items, React sees every single element as “changed” and re-renders all 100.
-
With keys (identity-based diffing): React uses the key to match old elements to new ones. If you prepend an item, React recognizes that the existing 100 elements are unchanged (same keys), creates only the new one, and leaves the rest untouched.
Example — why position-based diffing breaks:
// Initial render
<ul>
<li key={0}>Alice</li> {/* index 0 */}
<li key={1}>Bob</li> {/* index 1 */}
</ul>
// After prepending "Zara" — React sees EVERY element as "changed"
<ul>
<li key={0}>Zara</li> {/* index 0 — React thinks Alice became Zara */}
<li key={1}>Alice</li> {/* index 1 — React thinks Bob became Alice */}
<li key={2}>Bob</li> {/* index 2 — React thinks this is a new element */}
</ul>
Example — stable keys fix this:
const users = [
{ id: 'u3', name: 'Zara' },
{ id: 'u1', name: 'Alice' },
{ id: 'u2', name: 'Bob' },
];
// React matches by ID, not position — Alice and Bob are untouched
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
What makes a good key:
| Good ✅ | Bad ❌ |
|---|---|
Stable unique ID from data (user.id) |
Array index when list can reorder |
| UUID or database primary key | Math.random() (new key every render) |
| Slug or unique string identifier | Index when items can be inserted/deleted |
Key rules:
- Keys must be unique among siblings, not globally.
- Keys are not passed as props — the child cannot read
props.key. - Keys must be stable across renders; changing them forces a remount.
⚝ Try this example on CodeSandbox
Q. Can a child component access the key prop directly?
No. key is a reserved prop in React. React consumes it internally for reconciliation and does not pass it down to the component as part of props. If you try to read props.key inside a child, you will always get undefined.
Example: key is NOT accessible via props:
function ListItem({ id, name }) {
// ✅ 'id' and 'name' are accessible
// ❌ 'key' is NOT — it is stripped by React before props reach the component
return <li>{name}</li>;
}
function List() {
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
return (
<ul>
{users.map(user => (
<ListItem key={user.id} id={user.id} name={user.name} />
))}
</ul>
);
}
Workaround — pass the ID as a separate prop:
If the child needs the identifier value (e.g., to make an API call or handle a delete), pass it explicitly as a separate prop alongside key:
// Pass id separately so the child can use it
<ListItem key={user.id} id={user.id} name={user.name} />
Inside ListItem, props.id will work correctly, while props.key will always be undefined.
Why does React do this?
key is part of React's internal reconciliation mechanism, not application data. Exposing it as a prop would conflate two separate concerns — component identity (React's job) and component data. Keeping them separate makes the API cleaner and avoids accidental misuse.
Q. How do you avoid re-rendering the whole list unnecessarily?
When a parent re-renders, every child in a .map() list re-renders too — even if their individual data didn't change. There are several techniques to prevent this.
1. Use stable keys (prerequisite):
Always use stable, unique IDs as keys. This lets React skip DOM mutations for unchanged items during reconciliation.
{users.map(user => <UserCard key={user.id} user={user} />)}
2. Wrap list items with React.memo:
React.memo is a higher-order component that skips re-rendering if props are shallowly equal. Wrap the individual list-item component, not the list itself.
const UserCard = React.memo(function UserCard({ user }) {
console.log('rendered:', user.name);
return <li>{user.name}</li>;
});
Now only the UserCard whose user prop actually changed will re-render.
3. Stabilize callback props with useCallback:
If you pass a callback (e.g., onDelete) to each list item, a new function reference is created on every parent render — breaking React.memo shallow comparison. Wrap callbacks in useCallback to keep the reference stable.
const handleDelete = useCallback((id) => {
setUsers(prev => prev.filter(u => u.id !== id));
}, []); // stable reference — React.memo on child now works correctly
{users.map(user => (
<UserCard key={user.id} user={user} onDelete={handleDelete} />
))}
4. Avoid creating new object/array references in render:
Inline object or array literals inside JSX always produce a new reference, defeating React.memo.
// ❌ Bad — new object on every render
<UserCard key={user.id} style= user={user} />
// ✅ Good — stable reference defined outside render
const cardStyle = { color: 'red' };
<UserCard key={user.id} style={cardStyle} user={user} />
5. Keep state as flat and granular as possible:
If a single item changes and the whole list array reference changes, all memoized children will re-render if they receive the full item object. Prefer normalized state (e.g., a map of id → item) so mutations only touch the relevant slice.
// ❌ Array — updating one item rebuilds the whole array
const [users, setUsers] = useState([...]);
// ✅ Map — updating one entry does not disturb others
const [usersById, setUsersById] = useState({ u1: {...}, u2: {...} });
Q. How do you sort a list in React without mutating state?
The key rule is: never call .sort() directly on the state array, because Array.prototype.sort() sorts in place and mutates the original array. Instead, create a shallow copy first using the spread operator ([...arr]) or .slice(), then sort the copy.
Why mutation is dangerous in React:
React uses reference equality to detect state changes. If you mutate the existing array, the reference stays the same and React may not re-render. You also lose the ability to roll back to a previous state.
1. Basic sort — spread a copy first:
Example:
import { useState } from 'react';
const initialFruits = ['Banana', 'Apple', 'Cherry', 'Mango'];
export default function FruitList() {
const [fruits, setFruits] = useState(initialFruits);
const sortAscending = () => {
// ✅ Spread creates a new array — original state is untouched
setFruits([...fruits].sort((a, b) => a.localeCompare(b)));
};
const sortDescending = () => {
setFruits([...fruits].sort((a, b) => b.localeCompare(a)));
};
return (
<>
<button onClick={sortAscending}>Sort A → Z</button>
<button onClick={sortDescending}>Sort Z → A</button>
<ul>
{fruits.map((fruit) => (
<li key={fruit}>{fruit}</li>
))}
</ul>
</>
);
}
2. Sort an array of objects by a property:
Example:
const [users, setUsers] = useState([
{ id: 1, name: 'Priya', age: 28 },
{ id: 2, name: 'Alice', age: 34 },
{ id: 3, name: 'Bob', age: 22 },
]);
// Sort by age (ascending)
const sortByAge = () => {
setUsers(prev => [...prev].sort((a, b) => a.age - b.age));
};
// Sort by name (alphabetical)
const sortByName = () => {
setUsers(prev => [...prev].sort((a, b) => a.name.localeCompare(b.name)));
};
⚝ Try this example on CodeSandbox
Q. How do you update one item in a state list immutably?
To update a single item in a state array without mutating the original, use Array.prototype.map() to return a new array where only the matching item is replaced. Every other item is returned as-is.
.map()always returns a new array — the original is never mutated.- The spread
{ ...item, ...changes }creates a new object for the updated item, leaving all other fields intact. - Items that don't match are returned by reference, so React can skip re-rendering them (when used with
React.memo).
Example: Functional component with useState
import { useState } from 'react';
const initialTasks = [
{ id: 1, title: 'Buy groceries', done: false },
{ id: 2, title: 'Read a book', done: false },
{ id: 3, title: 'Go for a walk', done: false },
];
export default function TaskList() {
const [tasks, setTasks] = useState(initialTasks);
const toggleDone = (id: number) => {
setTasks(prevTasks =>
prevTasks.map(task =>
task.id === id ? { ...task, done: !task.done } : task
)
);
};
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
<span style=>
{task.title}
</span>
<button onClick={() => toggleDone(task.id)}>
{task.done ? 'Undo' : 'Done'}
</button>
</li>
))}
</ul>
);
}
Q. How do you handle very large lists in React apps?
Rendering thousands of DOM nodes at once causes slow initial paint, sluggish scrolling, and high memory usage. The core strategies are virtualization, pagination, infinite scrolling, and memoization.
1. Virtualization (windowing) — most impactful:
Only render the rows currently visible in the viewport. As the user scrolls, off-screen rows are unmounted and new ones are mounted. Libraries like react-window and react-virtual implement this efficiently.
import { List, RowComponentProps } from 'react-window';
const Row = ({ index, style }: RowComponentProps) => (
<div style={style}>Row {index}</div>
);
export default function BigList() {
return (
<List
style=
rowCount={100000}
rowHeight={25}
rowComponent={Row}
rowProps=
/>
);
}
Install: npm install react-window
For variable-height rows use VariableSizeList; for grids use FixedSizeGrid / VariableSizeGrid.
2. Pagination — load data in pages:
Fetch and render only one page of data at a time. Users navigate between pages explicitly. This is the simplest strategy and works well for tabular data.
import { useState } from 'react';
const PAGE_SIZE = 10;
const ALL_ITEMS = Array.from({ length: 100 }, (_, i) => ({ id: i + 1, name: `Item ${i + 1}` }));
export default function PaginatedList() {
const [page, setPage] = useState(1);
const totalPages = Math.ceil(ALL_ITEMS.length / PAGE_SIZE);
const visible = ALL_ITEMS.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
return (
<>
<ul>{visible.map(item => <li key={item.id}>{item.name}</li>)}</ul>
<button disabled={page === 1} onClick={() => setPage(p => p - 1)}>Prev</button>
<button disabled={page === totalPages} onClick={() => setPage(p => p + 1)}>Next</button>
</>
);
}
3. Infinite scrolling — append on demand:
Load the next batch of items when the user reaches the bottom. Use the IntersectionObserver API (or a library like react-intersection-observer) to detect when a sentinel element enters the viewport.
import { useState, useRef, useCallback } from 'react';
type Item = { id: number; name: string };
const ALL_ITEMS: Item[] = Array.from({ length: 100 }, (_, i) => ({ id: i + 1, name: `Item ${i + 1}` }));
const PAGE_SIZE = 10;
export default function InfiniteList() {
const [count, setCount] = useState(PAGE_SIZE);
const items = ALL_ITEMS.slice(0, count);
const hasMore = count < ALL_ITEMS.length;
const loadMore = () => setCount(c => Math.min(c + PAGE_SIZE, ALL_ITEMS.length));
const observer = useRef<IntersectionObserver | null>(null);
const sentinelRef = useCallback((node: Element | null) => {
if (observer.current) observer.current.disconnect();
observer.current = new IntersectionObserver(entries => {
if (entries[0].isIntersecting && hasMore) loadMore();
});
if (node) observer.current.observe(node);
}, [hasMore]);
return (
<ul>
{items.map((item: Item) => <li key={item.id}>{item.name}</li>)}
{hasMore && <li ref={sentinelRef} />}
</ul>
);
}
4. Memoization — avoid re-rendering unchanged rows:
Combine any of the above with React.memo and useCallback so that only changed rows re-render when the parent updates.
const ListRow = React.memo(function ListRow({ item, onSelect }) {
return <li onClick={() => onSelect(item.id)}>{item.name}</li>;
});
// In parent — stable callback reference
const handleSelect = useCallback((id) => {
setSelectedId(id);
}, []);
Strategy comparison:
| Strategy | Best for | Complexity |
|---|---|---|
| Virtualization | Any large list (1k+ items visible) | Medium |
| Pagination | Tabular data, server-side data | Low |
| Infinite scroll | Social feeds, search results | Medium |
| Memoization | Preventing unnecessary re-renders | Low |
For most apps: virtualize if all data is in memory (e.g. a local dataset), paginate or infinite-scroll if data is fetched from a server.
Q. How do Suspense/loading states integrate with list rendering?
React provides two complementary approaches for handling loading states when rendering lists: the traditional manual loading state pattern and the modern React Suspense pattern.
1. Manual loading state (classic approach)
Manage a loading boolean in state, show a spinner or skeleton while data is being fetched, then render the list once data arrives.
import { useState, useEffect } from 'react';
export default function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => {
setUsers(data);
setLoading(false);
})
.catch(err => {
setError(err.message);
setLoading(false);
});
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
2. React Suspense with data-fetching libraries
React.Suspense lets you declaratively specify a fallback UI while a child component is “suspended” (waiting for data). Libraries like React Query, SWR, and Relay integrate with Suspense natively.
import { Suspense } from 'react';
import { useSuspenseQuery } from '@tanstack/react-query';
// Child component — suspends automatically while fetching
function UserList() {
const { data: users } = useSuspenseQuery({
queryKey: ['users'],
queryFn: () =>
fetch('https://jsonplaceholder.typicode.com/users').then(r => r.json()),
});
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// Parent wraps the list in Suspense and provides a fallback
export default function App() {
return (
<Suspense fallback={<p>Loading users...</p>}>
<UserList />
</Suspense>
);
}
Q. How does rendering lists differ in Server Components vs Client Components
React 18+ (with frameworks like Next.js 13+) introduced Server Components (RSC) and retained Client Components. The two render lists in fundamentally different environments and with different constraints.
Core distinction
| Server Component | Client Component | |
|---|---|---|
| Runs on | Server (Node.js, Edge runtime) | Browser |
Can use hooks (useState, useEffect) |
❌ No | ✅ Yes |
| Can fetch data directly (DB, filesystem) | ✅ Yes | ❌ No (needs API) |
| Ships JS to the browser | ❌ No (HTML only) | ✅ Yes |
| Supports interactivity (click, input) | ❌ No | ✅ Yes |
| Default in Next.js App Router | ✅ Yes | Requires "use client" directive |
1. Rendering a list in a Server Component
Server Components can be async and fetch data directly from a database or API — no useEffect, no loading state, no client-side JavaScript shipped for the list itself.
// app/users/page.tsx (Next.js App Router — Server Component by default)
async function UsersPage() {
// Direct database or API call — runs only on the server
const res = await fetch('https://jsonplaceholder.typicode.com/users', {
cache: 'force-cache', // or 'no-store' for dynamic data
});
const users = await res.json();
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UsersPage;
- The list HTML is rendered on the server and streamed to the browser.
- No JavaScript is sent to the client for this component.
- No hydration cost — zero interactivity overhead.
2. Rendering a list in a Client Component
Client Components are required whenever the list is interactive — sorting, filtering, selecting, paginating on the client, or using any React hook.
'use client'; // marks this file as a Client Component
import { useState } from 'react';
type User = { id: number; name: string };
const USERS: User[] = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
export default function FilterableList() {
const [query, setQuery] = useState('');
const filtered = USERS.filter(user =>
user.name.toLowerCase().includes(query.toLowerCase())
);
return (
<>
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Search users…"
/>
<ul>
{filtered.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</>
);
}
# 10. REACT FORMS
Q. How dynamically generate menu options for <select> from an array?
Example:
import { useState } from "react";
export default function ColorPicker() {
const colors = ["Red", "Green", "Blue", "Black"];
const [selectedColor, setSelectedColor] = useState("");
return (
<div>
<label htmlFor="color">Choose a color: </label>
<select
id="color"
value={selectedColor}
onChange={(e) => setSelectedColor(e.target.value)}
>
<option value="">-- Select color --</option>
{colors.map((color) => (
<option key={color} value={color}>
{color}
</option>
))}
</select>
<p>Selected: {selectedColor || "None"}</p>
</div>
);
}
⚝ Try this example on CodeSandbox
Q. How would you create a form in React?
Create it as a controlled form: keep each input value in state, update state on change, and handle submit in one function.
Example:
import { useState } from "react";
const countries = ["Austria", "India", "Italy", "Russia", "United States"];
export default function App() {
const [form, setForm] = useState({
email: "",
password: "",
country: "",
acceptedTerms: false,
});
const handleChange = (event) => {
const { name, value, type, checked } = event.target;
setForm((prev) => ({
...prev,
[name]: type === "checkbox" ? checked : value,
}));
};
const handleSubmit = (event) => {
event.preventDefault();
console.log("Submitted data:", form);
// call API here
};
return (
<form onSubmit={handleSubmit}>
<h2>Sign Up</h2>
<label>
Email
<input
type="email"
name="email"
value={form.email}
onChange={handleChange}
required
/>
</label>
<label>
Password
<input
type="password"
name="password"
value={form.password}
onChange={handleChange}
required
minLength={6}
/>
</label>
<label>
Country
<select
name="country"
value={form.country}
onChange={handleChange}
required
>
<option value="">Select country</option>
{countries.map((country) => (
<option key={country} value={country}>
{country}
</option>
))}
</select>
</label>
<label>
<input
type="checkbox"
name="acceptedTerms"
checked={form.acceptedTerms}
onChange={handleChange}
/>
I accept terms
</label>
<button type="submit" disabled={!form.acceptedTerms}>
Submit
</button>
</form>
);
}
Output:
⚝ Try this example on CodeSandbox
Q. Why You Should Choose React Hook Form Over Formik and Redux-Form?
Below are the main reasons to recommend React Hook Form Over Formik and Redux-Form,
1. Isolate Component Re-Rendering:
React Hook Form isolates the component and avoids the other components from re-rending. This feature will improve the performance by avoiding unwanted rendering in other child components. However, libraries like Formik and Redux-Form re-render the other child components along with the form component.
2. Reduce Rendering:
Besides isolating the component, it also limits its own (form components) form re-rendering on particular events such as onChange, onBlur, etc.
3. Faster Mounting:
Mounting time is about 13% faster than Formik and 25% faster than Redux-Form. In other words, the form's DOM elements will be inserted into the DOM tree quicker, leading to fast rendering compared with other libraries.
4. Input Change Subscriptions:
React Hook Form allows you to subscribe to each input element without going through the re-rendering of each element inside the form component.
5. Typescript Support:
React Hook Form is built with TypeScript and can define a FormData type to support form values.
6. Less Code to Maintain:
The React Hook Form provides a hook called useForm(), consisting of methods and props handleSubmit, register, and errors. They would handle the submit events, the input via refs using register, and display any errors. However, in the other two libraries, you need to write your custom handlers for events and validations.
Q. What are controlled and uncontrolled components in React?
In a controlled component, form data is handled by a React component. The alternative is uncontrolled components, where form data is handled by the DOM itself.
1. Controlled Components:
In a controlled component, the form data is handled by the state within the component. The state within the component serves as “the single source of truth” for the input elements that are rendered by the component.
2. Uncontrolled Components:
Uncontrolled components act more like traditional HTML form elements. The data for each input element is stored in the DOM, not in the component. Instead of writing an event handler for all of your state updates, It uses ref to retrieve values from the DOM. Refs provide a way to access DOM nodes or React elements created in the render method.
Example:
import { useRef, useState } from "react";
export default function FormExamples() {
// Controlled state
const [name, setName] = useState("");
// Uncontrolled ref
const ageRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
console.log(`Controlled name: ${name}`);
console.log(`Uncontrolled name: ${name}`);
};
return (
<form onSubmit={handleSubmit}>
<h3>Controlled Input</h3>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter name"
/>
<h3>Uncontrolled Input</h3>
<input
type="number"
ref={ageRef}
defaultValue="18"
placeholder="Enter age"
/>
<button type="submit">Submit</button>
</form>
);
}
- Controlled:
nameis fully managed by React state (value+onChange). - Uncontrolled:
ageis managed by the DOM and read viarefon submit.
⚝ Try this example on CodeSandbox
Q. How do you set default value for uncontrolled components?
React provides defaultValue attribute that pre-populate the input field with the default Value without overriding any value input by the user.
Example:
/**
* React defaultValue
*/
import { useRef } from "react";
export default function UncontrolledForm() {
const nameRef = useRef(null);
const newsletterRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
console.log(
`Name: ${nameRef.current.value}, Newsletter: ${newsletterRef.current.checked}`
);
};
return (
<form onSubmit={handleSubmit}>
<input
ref={nameRef}
type="text"
defaultValue="Pradeep" // initial value only
/>
<label>
<input
ref={newsletterRef}
type="checkbox"
defaultChecked={true} // initial checked state
/>
Subscribe
</label>
<button type="submit">Submit</button>
</form>
);
}
⚝ Try this example on CodeSandbox
Q. How to validate form data in React?
Form validation in React is typically done inside the submit handler or as the user types. You can write custom validation logic or use a library like React Hook Form or Yup.
Example: Custom inline validation
import { useState } from "react";
export default function LoginForm() {
const [form, setForm] = useState({ email: "", password: "" });
const [errors, setErrors] = useState({});
const validate = () => {
const newErrors = {};
if (!form.email.includes("@")) {
newErrors.email = "Enter a valid email address.";
}
if (form.password.length < 6) {
newErrors.password = "Password must be at least 6 characters.";
}
return newErrors;
};
const handleChange = (e) => {
const { name, value } = e.target;
setForm((prev) => ({ ...prev, [name]: value }));
// Clear error as user types
setErrors((prev) => ({ ...prev, [name]: "" }));
};
const handleSubmit = (e) => {
e.preventDefault();
const validationErrors = validate();
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
return;
}
console.log("Form submitted:", form);
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="text"
name="email"
placeholder="Email"
value={form.email}
onChange={handleChange}
/>
{errors.email && <p style=>{errors.email}</p>}
</div>
<div>
<input
type="password"
name="password"
placeholder="Password"
value={form.password}
onChange={handleChange}
/>
{errors.password && <p style=>{errors.password}</p>}
</div>
<button type="submit">Login</button>
</form>
);
}
Example: Using React Hook Form + Yup
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
const schema = yup.object({
email: yup.string().email("Invalid email").required("Email is required"),
password: yup.string().min(6, "Min 6 characters").required(),
});
export default function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(schema),
});
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("email")} placeholder="Email" />
{errors.email && <p>{errors.email.message}</p>}
<input {...register("password")} type="password" placeholder="Password" />
{errors.password && <p>{errors.password.message}</p>}
<button type="submit">Login</button>
</form>
);
}
Q. How to reset a form in React?
1. Controlled Form Reset — Reset each piece of state back to its initial value:
import { useState } from "react";
const initialState = { name: "", email: "" };
export default function ContactForm() {
const [form, setForm] = useState(initialState);
const handleChange = (e) => {
const { name, value } = e.target;
setForm((prev) => ({ ...prev, [name]: value }));
};
const handleReset = () => setForm(initialState); // restore defaults
const handleSubmit = (e) => {
e.preventDefault();
console.log("Submitted:", form);
handleReset(); // optionally reset after submit
};
return (
<form onSubmit={handleSubmit}>
<input name="name" value={form.name} onChange={handleChange} placeholder="Name" />
<input name="email" value={form.email} onChange={handleChange} placeholder="Email" />
<button type="submit">Submit</button>
<button type="button" onClick={handleReset}>Reset</button>
</form>
);
}
2. Uncontrolled Form Reset — Use the native reset() method via a ref or the built-in type="reset" button:
import { useRef } from "react";
export default function UncontrolledForm() {
const formRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
const data = new FormData(formRef.current);
console.log(Object.fromEntries(data));
formRef.current.reset(); // native DOM reset
};
return (
<form ref={formRef} onSubmit={handleSubmit}>
<input name="username" placeholder="Username" />
<input name="email" type="email" placeholder="Email" />
<button type="submit">Submit</button>
<button type="reset">Reset</button> {/* native HTML reset */}
</form>
);
}
3. Using React Hook Form:
import { useForm } from "react-hook-form";
export default function MyForm() {
const { register, handleSubmit, reset } = useForm();
const onSubmit = (data) => {
console.log(data);
reset(); // resets all fields to defaultValues
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("name")} placeholder="Name" />
<button type="submit">Submit</button>
<button type="button" onClick={() => reset()}>Reset</button>
</form>
);
}
Q. How to handle file upload in React?
To handle file uploads in React, use an <input type="file" /> and store the selected file in state. Then send it to a server using FormData and fetch or Axios.
Example: Single file upload
import { useState } from "react";
export default function FileUpload() {
const [file, setFile] = useState(null);
const [preview, setPreview] = useState(null);
const [status, setStatus] = useState("");
const handleFileChange = (e) => {
const selected = e.target.files[0];
if (!selected) return;
setFile(selected);
// Show image preview
if (selected.type.startsWith("image/")) {
setPreview(URL.createObjectURL(selected));
}
};
const handleUpload = async (e) => {
e.preventDefault();
if (!file) return;
const formData = new FormData();
formData.append("file", file);
try {
setStatus("Uploading...");
const response = await fetch("/api/upload", {
method: "POST",
body: formData, // Do NOT set Content-Type header manually
});
if (!response.ok) throw new Error("Upload failed");
setStatus("Upload successful!");
} catch (err) {
setStatus(`Error: ${err.message}`);
}
};
return (
<form onSubmit={handleUpload}>
<input type="file" accept="image/*" onChange={handleFileChange} />
{preview && <img src={preview} alt="Preview" width={200} />}
<button type="submit" disabled={!file}>Upload</button>
<p>{status}</p>
</form>
);
}
Key Points:
- Use
e.target.files[0]to access the selected file from the input event. - Use
FormDatato send binary data to the server — do not manually setContent-Type; the browser sets the correctmultipart/form-databoundary automatically. - Use
URL.createObjectURL()for client-side image previews, and callURL.revokeObjectURL()on cleanup to avoid memory leaks. - For multiple files, add
multipleto the input and iterate overe.target.files.
Q. How to implement two-way data binding in React?
Two-way data binding means the UI and the application state stay in sync: when the state changes, the input updates, and when the user types, the state updates.
React doesn't have built-in two-way binding like Angular's ngModel, but you can achieve it manually using controlled components — pairing a value prop with an onChange handler.
Example: Controlled Input (Simulating Two-Way Binding)
import { useState } from 'react';
function TwoWayBindingExample() {
const [text, setText] = useState('');
return (
<div>
<input
type="text"
value={text} // State → UI (one direction)
onChange={(e) => setText(e.target.value)} // UI → State (other direction)
/>
<p>Live preview: {text}</p>
</div>
);
}
Example: Two-way binding with multiple fields
function ProfileForm() {
const [form, setForm] = useState({ name: '', email: '' });
const handleChange = (e) => {
const { name, value } = e.target;
setForm(prev => ({ ...prev, [name]: value }));
};
return (
<form>
<input name="name" value={form.name} onChange={handleChange} placeholder="Name" />
<input name="email" value={form.email} onChange={handleChange} placeholder="Email" />
<p>{form.name} — {form.email}</p>
</form>
);
}
Note on ReactLink:
ReactLink was an older React add-on (react-addons-linked-state-mixin) that provided syntactic sugar for two-way binding in class components. It has been deprecated and removed. The modern approach is to use controlled components as shown above, or a form library like react-hook-form.
Comparison with Angular:
<!-- Angular two-way binding -->
<input [(ngModel)]="name" />
<!-- React equivalent (manual) -->
<input value={name} onChange={(e) => setName(e.target.value)} />
Q. What is the rules property in react-hook-form?
In react-hook-form, the rules property is passed to the register() function (or the Controller component) to apply validation constraints to a form field. It follows the same API as HTML5 constraint validation and supports both built-in rules and custom validators.
Basic Syntax:
import { useForm } from 'react-hook-form';
function MyForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
return (
<form onSubmit={handleSubmit(console.log)}>
<input
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: 'Invalid email address',
},
maxLength: { value: 100, message: 'Max 100 characters' },
})}
/>
{errors.email && <p>{errors.email.message}</p>}
<input
type="number"
{...register('age', {
required: true,
min: { value: 18, message: 'Must be at least 18' },
max: { value: 100, message: 'Must be at most 100' },
validate: (value) => Number.isInteger(Number(value)) || 'Must be a whole number',
})}
/>
{errors.age && <p>{errors.age.message}</p>}
<button type="submit">Submit</button>
</form>
);
}
Supported rules properties:
| Rule | Type | Description |
|---|---|---|
required |
boolean \| string |
Makes the field mandatory |
min |
number \| object |
Minimum value (for numbers/dates) |
max |
number \| object |
Maximum value |
minLength |
number \| object |
Minimum string length |
maxLength |
number \| object |
Maximum string length |
pattern |
RegExp \| object |
Regex pattern validation |
validate |
Function \| object |
Custom validation function(s) |
Custom validate function:
register('password', {
validate: {
hasUppercase: (v) => /[A-Z]/.test(v) || 'Needs an uppercase letter',
hasNumber: (v) => /\d/.test(v) || 'Needs a number',
}
})
# 11. REACT CSS STYLING
Q. How to use styles in React.js?
React Components can add styling in the following ways:
1. Inline Styles:
Inline styles are applied directly to elements using the style attribute. In React, this attribute accepts a JavaScript object rather than a string.
- CamelCase Keys: CSS properties with hyphens (e.g., background-color) must be written in camelCase (e.g., backgroundColor).
- Syntax: Use double curly braces ``—the outer pair for JSX expression and the inner for the object literal.
- Use Case: Best for dynamic, state-based styles (like a progress bar width)
Example:
/**
* Inline Styling
*/
class HeaderComponent extends React.Component {
render() {
return (
<div>
<h1 style=>Header Component Style!</h1>
<p>Add a little style!</p>
</div>
);
}
}
⚝ Try this example on CodeSandbox
2. Styled-Components (CSS-in-JS)
This popular library allows you to write actual CSS code directly inside your JavaScript files using tagged template literals.
- Installation: Requires
npm install styled-components. - Benefits: It creates unique class names automatically and supports full CSS features like nesting and media queries.
Example:
/**
* Styled Components
*/
import styled from 'styled-components';
const Wrapper = styled.div`
color: white;
background: black;
&:hover { color: gold; }
`;
⚝ Try this example on CodeSandbox
3. CSS Stylesheet:
You can write your CSS styling in a separate file, just save the file with the .css file extension, and import it in your application.
Example:
/**
* App.css
*/
body {
background-color: #282c34;
color: white;
padding: 40px;
font-family: Arial;
text-align: center;
}
/**
* CSS Stylesheet
*/
import './App.css';
class HeaderComponent extends React.Component {
render() {
return (
<div>
<h1>Header Component Style!</h1>
<p>Add a little style!.</p>
</div>
);
}
}
4. CSS Modules:
CSS Modules allow you to scope CSS locally to a specific component, preventing global naming conflicts.
Example:
import styles from './Button.module.css';
function Button() {
return <button className={styles.error}>Click Me</button>;
}
Q. How to conditionally apply CSS classes in React JS?
Example: Using ternary operator
import { useState } from 'react'
function App() {
const [isRed, setIsRed] = useState(true)
return <p style=>Example Text</p>
}
Example: Using Logical AND (&&)
import { useState } from 'react'
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false)
return (
<>
<p className={`message ${isLoggedIn && 'active'}`}>
{isLoggedIn ? 'Welcome back!' : 'Please log in.'}
</p>
<button onClick={() => setIsLoggedIn(!isLoggedIn)}>
{isLoggedIn ? 'Logout' : 'Login'}
</button>
</>
)
}
⚝ Try this example on CodeSandbox
Q. How to combine multiple inline style objects?
Example: Using Spread operator
const box = {
color: "green",
fontSize: '23px'
}
const shadow = {
background: "orange",
boxShadow: "1px 1px 1px 1px #cccd"
}
export default function App(){
return (
<div style=>
<h1>Hello React</h1>
</div>
)
}
Example: Using Object.assign()
import { useState } from 'react'
export default function App() {
const [isActive, setIsActive] = useState(false)
const baseStyle = {
padding: '10px 20px',
borderRadius: '4px',
border: '1px solid #ccc',
cursor: 'pointer'
}
const activeStyle = {
background: 'blue',
color: 'white',
border: '1px solid blue'
}
const combinedStyle = Object.assign({}, baseStyle, isActive ? activeStyle : {})
return (
<div>
<div style={combinedStyle} onClick={() => setIsActive(!isActive)}>
{isActive ? 'Active' : 'Inactive'}
</div>
</div>
)
}
⚝ Try this example on CodeSandbox
Q. What are the popular package for animation in React JS?
Quick Comparison
| Package | Style | Best For |
|---|---|---|
| Framer Motion | Declarative | General UI, gestures |
| React Spring | Physics-based | Natural motion |
| React Transition Group | CSS-based | Enter/exit transitions |
| React Move | Data-driven | Data visualizations |
| React Reveal | Scroll-based | Landing pages |
Q. What is the benefit of styles modules?
In React, the primary benefit of using CSS Modules is that it provides local scoping for styles by default. This solves the common problem of global namespace collisions where styles from one component accidentally overwrite styles in another.
Benefits of CSS modules:
-
No Class Name Collisions: Each component gets unique, auto-generated class names at build time, so
.buttonin one component never conflicts with.buttonin another. -
Local Scope by Default: Styles are scoped to the component that imports them — changes in one file don't accidentally break other components.
-
Reuse the Same Class Names: You can use
.container,.title,.btnin every component's CSS file without worrying about conflicts. -
Maintainability: You can confidently update or delete a CSS file knowing it only affects the one component that imports it.
-
Dead Code Elimination: Bundlers can detect and remove unused CSS module classes since the relationship between JS and CSS is explicit.
Q. What are styled components?
styled-components is a CSS-in-JS library that lets you write actual CSS inside JavaScript using tagged template literals.
The styled-components comes with a collection of helper methods, each corresponding to a DOM node for example <h1>, <header>, <button>, and SVG elements like line and path. The helper methods are called with a chunk of CSS, using an obscure JavaScript feature known as “tagged template literals”.
Key Features
- Scoped styles: Auto-generated unique class names
- Dynamic styling: Props-based style logic
- Full CSS support: Nesting, media queries, pseudo-classes
- Theming:
ThemeProviderfor app-wide themes - No class naming: No need to manually manage class names
- SSR support: Works with server-side rendering
Example:
import styled from 'styled-components';
// This creates a button component with specific styles
const StyledButton = styled.button`
background: transparent;
border-radius: 3px;
border: 2px solid #bf4f74;
color: #bf4f74;
padding: 0.25em 1em;
`;
// Use it like any other React component
render(<StyledButton>Click Me</StyledButton>);
Q. How to use useSpring() for animation?
In the React Spring library, useSpring() is a custom hook used to create single-spring animations that move data from one state to another. Unlike traditional CSS animations that rely on fixed durations and easing curves, useSpring uses spring physics—such as tension, friction, and mass—to produce motion that feels natural and mimics the real world.
Basic Implementation:
The most common way to use useSpring is by passing an object with from (initial state) and to (target state) properties.
Example:
/**
* useSpring()
*/
import { useSpring, animated } from '@react-spring/web';
function FadeIn() {
const styles = useSpring({
from: { opacity: 0 },
to: { opacity: 1 },
});
return <animated.div style={styles}>I will fade in!</animated.div>;
}
2. Interactive Animations:
You can trigger animations based on state changes (like hovers or clicks) by passing conditional values directly into the hook.
Example:
const [clicked, setClicked] = useState(false);
const springs = useSpring({
width: clicked ? '300px' : '100px',
background: clicked ? 'blue' : 'red',
config: { tension: 200, friction: 20 } // Adjust physics here
});
return (
<animated.div
onClick={() => setClicked(!clicked)}
style={springs}
/>
);
Q. How do you apply vendor prefixes to inline styles in React?
React does not automatically apply vendor prefixes to inline styles. You must either add them manually as camelCased properties or use a third-party library to handle them at runtime.
Manual Prefixing
To apply prefixes manually, add them as separate properties in your style object using camelCase.
- Webkit, Moz, and O: These must start with a capital letter (e.g.,
WebkitTransform). - ms: This is the only prefix that should stay lowercase (e.g.,
msTransform).
Example:
const myStyle = {
transform: 'rotate(45deg)',
WebkitTransform: 'rotate(45deg)', // Chrome, Safari, newer Opera
msTransform: 'rotate(45deg)' // Internet Explorer 9
};
return <div style={myStyle}>Hello World</div>;
⚝ Try this example on CodeSandbox
Q. How to display style based on props value?
When using Styled Components, you can access props by passing a function into the tagged template literal. This is the most common way to handle dynamic styles in React.
1. Simple Ternary (Toggle):
import styled from 'styled-components'
const Button = styled.button`
background: ${props => props.primary ? 'blue' : 'gray'}
color: 'white'
`;
function MyPureComponent(props) {
return (
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
)
}
2. Multi-line Blocks (CSS Helper)
If you need to apply a whole block of CSS rules based on a prop, use the css helper from the library. This keeps the code clean and ensures syntax highlighting works.
import styled, { css } from 'styled-components';
const Alert = styled.div`
padding: 10px;
${props => props.urgent && css`
background: red;
font-weight: bold;
border: 2px solid darkred;
`}
`;
// Usage
<Alert urgent>Emergency!</Alert>
3. Complex Mapping (The “Switch” Pattern)
If your component has multiple states (e.g., ‘success’, ‘warning’, ‘error’), use a mapping object to avoid long if/else chains.
const themeColors = {
success: 'green',
warning: 'orange',
error: 'red'
};
const StatusBox = styled.div`
background: ${props => themeColors[props.status] || 'gray'};
`;
// Usage
<StatusBox status="success">Task Complete</StatusBox>
# 12. REACT ROUTER
Q. What is React Router?
React router enables the navigation among views of various components in a React Application, allows changing the browser URL, and keeps the UI in sync with the URL. It has a simple API with powerful features like lazy loading, dynamic route matching, and location transition handling.
Core components (v7):
- BrowserRouter: Parent wrapper; uses HTML5 History API to sync UI with URL
- Routes: Container for all Route definitions; matches the best route
- Route: Maps a URL path to a component
- Link / NavLink: Declarative navigation; NavLink adds active styling
Example:
/**
* React Router v7
*/
import { BrowserRouter, Route, Routes, NavLink } from "react-router-dom";
/**
* Home Component
*/
const Home = () => {
return <h1>Home Page</h1>;
};
/**
* Contacts Component
*/
const Contacts = () => {
return <h1>Contact Page</h1>;
};
/**
* App Component
*/
export default function App() {
return (
<div className="App">
<BrowserRouter>
<div className="navbar">
<NavLink to={"/"}>Home</NavLink>
<NavLink to={"/contact"}>Contact Us</NavLink>
</div>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/contact" element={<Contacts />} />
</Routes>
</BrowserRouter>
</div>
);
}
⚝ Try this example on CodeSandbox
Q. What are the components of react router?
The main components of React router are
1. BrowserRouter:
BrowserRouter is a router implementation that uses the HTML5 history API (pushState, replaceState and popstate event) to keep your UI in sync with the URL. It is the parent component that is used to store all of the other components.
2. Routes:
It's a new component introduced in the v6 and a upgrade of the component. The main advantages of Routes over Switch that routes are chosen based on the best match instead of being traversed in order.
3. Route:
Route is the conditionally shown component that renders some UI when its path matches the current URL.
4. Link:
Link component is used to create links to different routes and implement navigation around the application. It works like HTML anchor tag.
5. NavLink:
Adds active styling to the link when its route matches the current URL
6. Outlet:
A critical component for nested routing. It acts as a placeholder within a parent route's component, indicating where child route elements should be rendered.
Q. What is the difference between NavLink and Link?
The <Link> component is used to navigate the different routes on the site. But <NavLink> is used to add the style attributes to the active routes.
Link:
Renders an anchor tag that navigates without a full page reload. No awareness of whether the route is currently active.
<Link to="/">Home</Link>
NavLink:
Same as Link, but automatically applies an active CSS class (or custom class) when the to path matches the current URL.
<NavLink to="/" activeClassName="active">Home</NavLink>
Example:
index.css
.active {
color: blue;
}
Routes.js
import { BrowserRouter as Router, Routes, Route, NavLink } from 'react-router-dom'
import './index.css'
import App from './App'
import Users from './users'
import Contact from './contact'
import Notfound from './notfound'
const nav = ({ isActive }) => isActive ? 'active' : undefined
const AppRoutes = () => (
<Router>
<div>
<ul>
<li><NavLink className={nav} to="/">Home</NavLink></li>
<li><NavLink className={nav} to="/users">Users</NavLink></li>
<li><NavLink className={nav} to="/contact">Contact</NavLink></li>
</ul>
<hr />
<Routes>
<Route path="/" element={<App />} />
<Route path="/users" element={<Users />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<Notfound />} />
</Routes>
</div>
</Router>
)
export default AppRoutes;
Note:
In React Router v7, NavLink uses a function for className instead of activeClassName.
Q. How React Router is different from history library?
React Router is a high-level, React-specific routing library built on top of the low-level history library. While the history library handles raw browser history manipulation, React Router adds declarative routing, component rendering, and React hooks on top of it.
Key Differences:
| Feature | history library |
React Router v7 |
|---|---|---|
| Level | Low-level | High-level |
| React awareness | None | React-specific |
| Navigation | history.push('/path') |
useNavigate() hook |
| Location access | history.location |
useLocation() hook |
| URL params | Manual | useParams() hook |
| UI sync | Manual | Automatic |
React Router v7 internalizes the history dependency — you no longer install or interact with it directly. All navigation is handled through hooks.
// <BrowserRouter> — clean URLs using HTML5 History API
http://example.com/about
// <HashRouter> — hash-based URLs for static hosting / legacy browsers
http://example.com/#/about
The <BrowserRouter> is preferred for modern apps as it uses the HTML5 History API. Use <HashRouter> only when you cannot configure the server to handle all routes (e.g., static file hosting).
Example (React Router v7):
// src/index.js
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
createRoot(document.getElementById("root")).render(
<BrowserRouter>
<App />
</BrowserRouter>
);
Navigating programmatically in v7:
import { useNavigate, useLocation } from "react-router-dom";
function MyComponent() {
const navigate = useNavigate(); // replaces history.push / history.replace
const location = useLocation(); // replaces history.location
return (
<button onClick={() => navigate("/home")}>Go Home</button>
);
}
In React Router v7, history.goBack() and history.goForward() are replaced by navigate(-1) and navigate(1) respectively.
Q. How to use useNavigate() in React Router v7?
The useNavigate() hook is introduced in React Router v7 to replace the useHistory() hook. In the earlier version, the useHistory() hook accesses the React Router history object and navigates to the other routers using the push() or replace() methods.
The useNavigate() hook returns a function that lets you navigate programmatically, for example after a form is submitted. If using replace: true, the navigation will replace the current entry in the history stack instead of adding a new one.
Example:
/**
* useNavigate()
*/
import { useNavigate } from "react-router-dom";
function Home() {
const navigate = useNavigate();
return (
<button onClick={() => navigate("/profile")}>
Go to Profile
</button>
);
}
⚝ Try this example on CodeSandbox
Q. How to get parameter value from query string in React?
1. useSearchParams:
The useSearchParams hook is a feature provided by libraries such as React Router and Next.js that allows React components to read and update the query string parameters (the portion of the URL after the ? symbol).
This hook is essential for managing URL-based state, which keeps the application's state persistent when the page is reloaded or the link is shared
Usage:
searchParams.get("key"):Get a single value, returns null if missingsearchParams.getAll("key"):Get all values for a key (multi-value params)searchParams.has("key"):Check if param existssetSearchParams({ key: val }):Update the query string (triggers re-render)
Example:
// http://localhost:3000/search?q=react
import { useSearchParams } from "react-router-dom";
function SearchPage() {
const [searchParams] = useSearchParams();
// Get a specific parameter by its key
const query = searchParams.get("q"); // URL: /search?q=react
return <div>Search results for: {query}</div>;
}
2. useParams:
useParams is a React Router hook used to extract dynamic parameters from the current URL path. These parameters are defined in your route configuration using a colon (:) followed by a variable name.
Example:
// http://localhost:3000/:userId
import { useParams } from "react-router-dom";
function Profile() {
// Extract userId from the URL path
const { userId } = useParams();
return <h1>Viewing profile for user: {userId}</h1>;
}
⚝ Try this example on CodeSandbox
Q. How to access history object in React Router v7?
The useNavigate() hook has been added to React Router v7 to replace the useHistory() hook.
Difference:
| v5 (useHistory) | v7 (useNavigate) |
|---|---|
history.push('/path') |
navigate('/path') |
history.replace('/path') |
navigate('/path', { replace: true }) |
history.goBack() |
navigate(-1) |
history.goForward() |
navigate(1) |
Example:
/**
* React Router
*/
import { BrowserRouter, Routes, NavLink, Route, useParams, useNavigate } from "react-router-dom";
export default function App() {
return (
<BrowserRouter>
<div>
<ul>
<li><NavLink to="/">Home</NavLink></li>
<li><NavLink to="/user/Bhavya/bhavyasingh@email.com">User Profile</NavLink></li>
</ul>
<Routes>
<Route path="/user/:name/:email" element={<User />} />
<Route path="/" element={<Home />} />
</Routes>
<HomeButton />
</div>
</BrowserRouter>
);
}
function Home() {
return <h2>Welcome Home</h2>;
}
function User() {
let { name, email } = useParams();
return (
<h2>Name: {name} <br /> Email: {email}</h2>
);
}
function HomeButton() {
const navigate = useNavigate();
function handleClick() {
navigate("/");
}
return (
<>
<button type="button" onClick={handleClick}>Go Home</button>
</>
);
}
⚝ Try this example on CodeSandbox
Q. How to perform automatic redirect in React.js?
In contrast to the Navigate component and its declarative redirect, we can perform a programmatic redirect by using React Router's useNavigate() Hook:
Example: Using <Navigate> component (declarative)
/**
* Automatic Redirect in React Router v7
*/
import {
BrowserRouter,
Routes,
Route,
Navigate,
NavLink,
} from "react-router-dom";
// --- Approach 1: <Navigate> component (declarative) ------------------------------
function AppWithNavigate() {
return (
<BrowserRouter>
<nav style=>
<NavLink to="/home">Home</NavLink>
<NavLink to="/about">About</NavLink>
<NavLink to="/help">Help</NavLink>
</nav>
<Routes>
{/* Visiting "/" automatically redirects to "/home" */}
<Route index element={<Navigate replace to="/home" />} />
<Route path="home" element={<h1>Home Page</h1>} />
<Route path="about" element={<h1>About Page</h1>} />
<Route path="help" element={<h1>Help Page</h1>} />
</Routes>
</BrowserRouter>
);
}
export default AppWithNavigate;
Example: Using redirect() in a loader (v7 data router)
// --- Approach 2: redirect() in a loader (v7 data router) ---------------------
import { createBrowserRouter, RouterProvider, redirect } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
// loader runs before rendering; returning redirect() sends the user elsewhere
loader: () => redirect("/home"),
},
{
path: "/home",
element: <h1>Home Page</h1>,
},
{
path: "/about",
element: <h1>About Page</h1>,
},
]);
function AppWithLoaderRedirect() {
return <RouterProvider router={router} />;
}
export default AppWithLoaderRedirect;
Note:
To keep the history clean, you should set replace prop. This will avoid extra redirects after the user click back.
⚝ Try this example on CodeSandbox
Q. How to pass additional data while redirecting to a route in React?
Using Link:
Example:
/**
* Pass additional data while redirecting
*/
import { BrowserRouter, Link, Route, Routes, useLocation } from "react-router-dom";
/**
* View User Component
*/
function ViewUser() {
const location = useLocation();
return (
<>
<h2>User Details</h2>
<div>Name:{location.state.name}</div>
<div>Email:{location.state.email}</div>
</>
);
}
/**
* User Component
*/
function User() {
return (
<div>
<h2>Pass additional data while redirecting</h2>
<Link
to="/view-user"
state=
>
<button>View User</button>
</Link>
</div>
);
}
/**
* App Component
*/
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route exact path="/" element={<User />} />
<Route exact path="/user" element={<User />} />
<Route exact path="/view-user" element={<ViewUser />} />
</Routes>
</BrowserRouter>
);
}
⚝ Try this example on CodeSandbox
Q. How to pass props in React router v7?
React Router uses a declarative, component-based approach to routing. Route allows you to map URL paths to different React components.
Example:
/**
* pass props directly via the element prop
*/
import { BrowserRouter, Routes, Route, NavLink } from "react-router-dom";
export function Greeting(props) {
const { text } = props;
return (
<>
<h2>Greetings Page</h2>
<p>{text}</p>
</>
);
}
const RouterExample = () => <h2>Home Page</h2>;
const App = () => (
<BrowserRouter>
<ul>
<li><NavLink to="/">Home</NavLink></li>
<li><NavLink to="/greeting/pradeep">Greeting</NavLink></li>
</ul>
<hr />
<Routes>
<Route exact path="/" element={<RouterExample />} />
<Route path="/greeting/:name" element={<Greeting text="Hello World" />} />
</Routes>
</BrowserRouter>
);
export default App;
⚝ Try this example on CodeSandbox
Q. How to get query parameters in react routing?
1. useParams() — for dynamic path segments
Used when the parameter is part of the URL path, defined with : in the route.
2. useSearchParams() — for query string parameters
Used when the parameter is in the query string (after ?).
Example:
/**
* useParams()
*/
import { BrowserRouter, Route, Routes, Link, useParams } from "react-router-dom";
export default function App() {
return (
<BrowserRouter>
<div>
<ul>
<li><Link to="/home">Home</Link></li>
<li><Link to="/contact-us">Contact Us</Link></li>
<li><Link to="/help">Help</Link></li>
</ul>
<Routes>
<Route path="/:id" element={<Child />} />
</Routes>
</div>
</BrowserRouter>
);
}
function Child() {
// `useParams` hook used here to access parameters
let { id } = useParams();
return <h2>Parameter: {id}</h2>;
}
⚝ Try this example on CodeSandbox
Q. What is the difference between HashRouter and BrowserRouter in React?
1. BrowserRouter:
- The widely popular router and a router for modern browsers which user HTML5 pushState API. (i.e.
pushState,replaceStateandpopStateAPI). - It routes as normal URL in browser, you can't differentiate whether it is server rendered page or client rendered page through the URL.
- It assumes, your server handles all the request URL (eg.,
/,/about) and points to rootindex.html. From there, BrowserRouter take care of routing the relevant page. - It accepts
forceRefreshprops to support legacy browsers which doesn't support HTML5 pushState API
Syntax:
/**
* https://example.com/home
* https://example.com/about
*/
<BrowserRouter
basename={optionalString}
forceRefresh={optionalBool}
getUserConfirmation={optionalFunc}
keyLength={optionalNumber}
>
<App />
</BrowserRouter>
Example:
/**
* BrowserRouter()
*/
import { Link, BrowserRouter, Routes, Route } from "react-router-dom";
const HomePage = () => {
return <h2>Home Page</h2>;
};
const AboutPage = () => {
return <h2>About Page</h2>;
};
export default function App() {
return (
<section className="App">
<BrowserRouter>
<ul>
<li><Link to="/home">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<Routes>
<Route exact path="/home" element={<HomePage />} />
<Route exact path="/about" element={<AboutPage />} />
</Routes>
</BrowserRouter>
</section>
);
}
⚝ Try this example on CodeSandbox
2. HashRouter:
- A router which uses client side hash routing.
- Whenever, there is a new route get rendered, it updated the browser URL with hash routes. (eg.,
/#/about) - Hash portion of the URL won't be handled by server, server will always send the
index.htmlfor every request and ignore hash value. Hash value will be handled by react router. - It is used to support legacy browsers which usually doesn't support HTML
pushStateAPI
Syntax:
/**
* https://example.com/#/home
* https://example.com/#/about
*/
<HashRouter
basename={optionalString}
getUserConfirmation={optionalFunc}
hashType={optionalString}
>
<App />
</HashRouter>
Example:
/**
* HashRouter()
*/
import { Link, HashRouter, Routes, Route } from "react-router-dom";
const HomePage = () => {
return <h2>Home Page</h2>;
};
const AboutPage = () => {
return <h2>About Page</h2>;
};
export default function App() {
return (
<section className="App">
<HashRouter>
<ul>
<li><Link to="/home">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<Routes>
<Route exact path="/home" element={<HomePage />} />
<Route exact path="/about" element={<AboutPage />} />
</Routes>
</HashRouter>
</section>
);
}
⚝ Try this example on CodeSandbox
Q. What is route based code splitting?
Route based code splitting is essential during the page transitions on the web, which takes some amount of time to load. Here is an example of how to setup route-based code splitting into the app using React Router with React.lazy.
Key Benefits
- Faster Initial Load: By shrinking the initial JavaScript payload, the time to first render is significantly reduced.
- Reduced Bandwidth: Users only download the code for the parts of the site they actually visit.
- Better Caching: When you update one page, only the chunk for that specific route needs to be re-downloaded by returning users, while others remain cached.
- Improved Interactivity: Smaller bundles are parsed and executed faster by the browser, leading to a better Interaction to Next Paint (INP)
Example:
/**
* Lazy Loading
*/
import React, { Suspense, lazy } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
const Home = lazy(() => import("./Home"));
const About = lazy(() => import("./About"));
export default function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
⚝ Try this example on CodeSandbox
Q. What is lazy function in React?
React.lazy() makes it easy to create components that are loaded using dynamic import() but are rendered like regular components. This will automatically cause the bundle containing the component to be loaded when the component is rendered.
React.lazy() takes a function as its argument that must return a promise by calling import() to load the component. The returned Promise resolves to a module with a default export containing the React component.
Example:
import React, { lazy } from 'react'
const MyComponent = React.lazy(() => import('./MyComponent'))
const App = () => {
<div>
<MyComponent />
</div>
}
Q. What is code-splitting?
Code-Splitting is a feature supported by bundlers like Webpack, Rollup and Browserify ( via factor-bundle ) which can create multiple bundles that can be dynamically loaded at runtime.
Code splitting uses React.lazy and Suspense library, which helps to load a dependency lazily and only load it when needed by the user. The code splitting improves:
- The performance of the app
- The impact on memory
- The downloaded Kilobytes (or Megabytes) size
React.lazy and Suspense:
The React.lazy function allows us to render a dynamic import as a regular component. The suspense component is responsible for handling the output when the lazy component is fetched and rendered.
Example:
import React, { Suspense } from 'react';
const UsersComponent = React.lazy(() => import('./UsersComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<UsersComponent />
</Suspense>
</div>
);
}
Q. What is dynamic import in react?
React.lazy() wraps a dynamic import() call. The import returns a Promise that resolves to a module with a default export of a React component. The lazy component must be rendered inside <Suspense> to show a fallback while loading.
Example:
import React, { Suspense } from 'react';
const UsersComponent = React.lazy(() => import('./UsersComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<UsersComponent />
</Suspense>
</div>
);
}
Q. What are loadable components?
If you want to do code-splitting in a server rendered app, it is recommend to use Loadable Components because React.lazy and Suspense is not available for server-side rendering (SSR). Loadable lets you render a dynamic import as a regular component.
Installation:
npm install @loadable/component
Key Features
- Automatic Chunk Naming: It generates meaningful names for your code chunks automatically, which is helpful for debugging.
- Error Handling: It works seamlessly with React Error Boundaries to handle network failures or loading errors gracefully.
- Developer Experience: The API is designed to be simple, allowing you to wrap an import in a
loadable()function and use it like a regular component.
Example:
import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import loadable from '@loadable/component'
// 1. Define your loadable components
// The fallback prop shows a UI while the component is downloading
const Home = loadable(() => import('./routes/Home'), {
fallback: <div>Loading Home...</div>,
})
const Dashboard = loadable(() => import('./routes/Dashboard'), {
fallback: <div>Loading Dashboard...</div>,
})
const App = () => (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/dashboard" component={Dashboard} />
</Switch>
</Router>
)
export default App
Q. What is the difference between SPA and MPA?
Single Page Application (SPA):
An SPA loads a single HTML page and dynamically updates the content using JavaScript without doing a full page reload. React is the most popular library for building SPAs.
- The browser loads one HTML file on the first visit, then JavaScript takes over all routing and rendering.
- Navigation between “pages” is handled client-side by the router (e.g., React Router).
- Only the data that changes is fetched from the server (via API calls).
Multi-Page Application (MPA):
An MPA serves a separate HTML page for each route. Every navigation triggers a full round-trip to the server, which responds with a complete HTML document.
- Traditional approach used by most websites and frameworks (PHP, Ruby on Rails, Django, etc.).
- Each URL has its own server-rendered HTML page.
- Suitable for content-heavy websites with many distinct pages.
Comparison:
| Feature | SPA | MPA |
|---|---|---|
| Page reloads | No (virtual navigation) | Yes (full page reload) |
| Initial load | Slower (loads entire app bundle) | Faster (loads only current page) |
| Subsequent navigation | Fast (no server round-trip) | Slower (new HTML each time) |
| SEO | Harder (requires SSR or pre-rendering) | Easier (server renders full HTML) |
| State management | Client-side (Context, Redux, etc.) | Mostly server-side |
| Development complexity | Higher | Lower |
| Examples | Gmail, Twitter, React apps | Wikipedia, e-commerce product pages |
React's Position:
React is primarily used for SPAs, but can support MPA patterns via:
- Server-Side Rendering (SSR) with Next.js — renders HTML on the server per request.
- Static Site Generation (SSG) with Next.js or Gatsby — pre-renders pages at build time.
- React Router with
createBrowserRouter— enables file-system-like routing within a SPA.
When to choose:
- SPA: Dashboards, admin panels, web apps with rich interactivity (e.g., email clients, project management tools).
- MPA: Content sites, e-commerce, blogs, SEO-critical pages, or apps where each page is largely independent.
# 13. React RESTful API
Q. How to make a API calls in React?
In React, API calls are typically made using the Fetch API (built-in) or Axios (a popular third-party library). These calls are usually placed within the useEffect hook to trigger when a component mounts or when specific dependencies change
1. Using the Fetch API (Built-in)
The native fetch() method requires two steps: one to make the request and another to parse the response into JSON
Example:
/**
* API call using fetch()
*/
import React, { useState, useEffect } from 'react';
// Example fetching data inside useEffect
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then(data => setUsers(data))
.catch(err => setError(err.message));
}, []);
2. Using Axios (Recommended for Larger Apps)
Axios simplifies the process by automatically parsing JSON and throwing errors for non-2xx status codes.
Example:
import axios from 'axios';
// Example using Axios inside useEffect
useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => setUsers(response.data))
.catch(err => setError(err.message));
}, []);
Key Differences
-
JSON Parsing:
fetch()requires manual parsing (.json()), whereas Axios returns the parsed data in response.data. -
Error Detection:
fetch()requires checkingresponse.ok, while Axios automatically triggers the catch block fornon-2xxstatus codes.
Best Practices:
- Keep API calls in
useEffect(or a custom hook). - Always handle loading, error, and cleanup.
- Use stable keys while rendering list data.
- For larger apps, consider React Query or SWR for caching/retries.
⚝ Try this example on CodeSandbox
Q. How to display API data using Axios in React?
Axios is a promise based HTTP client for making HTTP requests from a browser to any web server. Use Axios inside useEffect, save response data in state, and render it with map.
Example:
/**
* GET Request using Axios
*/
import { useEffect, useState } from "react";
import axios from "axios";
interface User {
id: number;
name: string;
email: string;
}
export default function App() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await axios.get("https://jsonplaceholder.typicode.com/users");
setUsers(response.data);
} catch (error) {
setError("Failed to fetch users: " + (error as Error).message);
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>{error}</p>;
return (
<div>
<h2>User List</h2>
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} - {user.email}
</li>
))}
</ul>
</div>
);
}
⚝ Try this example on CodeSandbox
Q. How to make a post call using Axios in React?
Use Axios post inside a submit handler, then update state with the response.
Example:
/**
* POST Request using Axios
*/
import { useState } from "react";
import axios, { AxiosError } from "axios";
interface CreatedUser {
id: number;
name: string;
}
export default function CreateUser() {
const [name, setName] = useState("");
const [result, setResult] = useState<CreatedUser | null>(null);
const [error, setError] = useState("");
const handleSubmit = async (event: React.SubmitEvent<HTMLFormElement>) => {
event.preventDefault();
setError("");
setResult(null);
try {
const response = await axios.post(
"https://jsonplaceholder.typicode.com/users",
{ name } // request body
);
setResult(response.data);
setName("");
} catch (err) {
const axiosErr = err as AxiosError;
setError("Failed to create user" + (axiosErr.response ? `: ${axiosErr.response.statusText}` : ""));
}
};
return (
<div>
<h2>Create User</h2>
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter name"
required
/>
<button type="submit">Submit</button>
</form>
{error && <p>{error}</p>}
{result && (
<div>
<h3>Created:</h3>
<p>ID: {result.id}</p>
<p>Name: {result.name}</p>
</div>
)}
</div>
);
}
⚝ Try this example on CodeSandbox
Q. What are the benefits of using Axios() over Fetch() for making http requests?
Axios is a third-party library that offers several high-level conveniences for handling HTTP requests, whereas the Fetch API is a native, low-level browser tool that often requires more manual setup.
Key Benefits of Axios
-
Automatic JSON Transformation: Axios automatically stringifies your request body and parses the response data into a JavaScript object. With Fetch, you must manually call
JSON.stringify()on requests and.json()on responses. -
Built-in Error Handling: Axios automatically rejects the promise if the server returns a status code outside the 2xx range (like 404 or 500). Fetch only rejects on network failures; otherwise, you must manually check response.ok.
-
Interceptors: You can use Axios Interceptors to globally modify requests or responses before they reach the application, which is ideal for automatically adding authentication tokens or logging.
-
Request Timeouts: Setting a timeout in Axios is as simple as adding a timeout property (in milliseconds) to the config. Fetch requires using the more complex AbortController interface to achieve this.
-
Progress Tracking: Axios has built-in support for monitoring upload and download progress via onUploadProgress and onDownloadProgress events, which is particularly useful for file management.
-
Wider Compatibility: Because Axios uses XMLHttpRequest under the hood, it supports older browsers like IE11 natively, while Fetch requires a polyfill for legacy support.
Q. How does Axios Interceptors work in React?
Axios Interceptors act as middleware for your HTTP requests and responses. They allow you to define global logic that executes automatically before a request is sent to the server or after a response is received, but before it reaches your component's .then() or .catch() blocks.
1. Request Interceptor:
Used to modify outgoing request configurations. Common tasks include automatically attaching Authorization headers (like JWT tokens) or logging request metadata for debugging.
Example:
axios.interceptors.request.use(
(request) => {
const token = localStorage.getItem('token');
if (token) {
request.headers['Authorization'] = `Bearer ${token}`;
}
return request; // must return the request
},
(error) => Promise.reject(error)
);
2. Response Interceptor:
Used to process incoming data or handle errors globally. This is ideal for centralized error management, such as redirecting a user to login if they receive a 401 Unauthorized error or refreshing expired tokens.
Example:
axios.interceptors.response.use(
(response) => response, // pass through successful responses
(error) => {
if (error.response?.status === 401) {
// redirect to login
}
return Promise.reject(error);
}
);
Implementation in React:
Typically configured once in a shared axiosInstance.js file or in a top-level component/effect:
Example:
// axiosInstance.js
import axios from 'axios';
const api = axios.create({ baseURL: 'https://api.example.com' });
api.interceptors.request.use((config) => {
config.headers['Authorization'] = `Bearer ${localStorage.getItem('token')}`;
return config;
});
api.interceptors.response.use(
(res) => res,
(err) => {
if (err.response?.status === 403) console.error('Forbidden');
return Promise.reject(err);
}
);
export default api;
Q. How to do caching in React?
Caching in React is typically handled through three distinct layers: Memoization (internal logic), Data Fetching (network requests), and Browser/Persistent Storage (long-term data)
1. In-Component Memoization
These built-in hooks prevent expensive calculations or component re-renders when inputs haven't changed.
- useMemo: Caches the result of a calculation. Use it for heavy data processing to avoid recalculating on every render.
Example:
const sortedList = useMemo(() => {
return items.sort((a, b) => a.price - b.price);
}, [items]);
- useCallback: Caches a function definition between re-renders. This is crucial when passing functions to optimized child components to prevent “shallow comparison” failures.
It memoizes a function instance, preventing unnecessary re-renders of child components.
Example:
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);
- React.memo: A higher-order component that skips re-rendering a component if its props remain the same.
Example:
const MyComponent = React.memo(({ name }) => {
return <div>{name}</div>;
});
2. Data & Network Caching
Managing API responses effectively is the most common “caching” need in React.
- TanStack Query (React Query): The industry standard for client-side caching. It uses a queryKey as a unique identifier to store and retrieve data. Features include staleTime (how long data is “fresh”) and gcTime (how long it stays in memory after being unused).
Example:
// TanStack Query example
const { data } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
staleTime: 5 * 60 * 1000, // cache for 5 minutes
});
- React cache() function: A newer React 18+ feature specifically for Server Components. It memoizes data requests within a single server request lifecycle to prevent “waterfall” performance issues.
Example:
import { cache } from 'react';
const getUser = cache(async (id) => {
return await db.users.findById(id);
});
- SWR (Stale-While-Revalidate): is a React hook library by Vercel for data fetching. It simplifies the process by handling caching, revalidation, and loading states automatically.
Example:
import useSWR from 'swr';
interface User {
id: number;
name: string;
email: string;
phone: string;
}
// 1. Define a fetcher function (can use fetch, axios, etc.)
const fetcher = (url: string) => fetch(url).then((res) => res.json());
function Profile() {
// 2. Use the hook with a unique key (URL) and the fetcher
const { data, error, isLoading } = useSWR<User[]>('https://jsonplaceholder.typicode.com/users', fetcher);
// 3. Handle loading and error states
if (error) return <div>Failed to load</div>;
if (isLoading) return <div>Loading...</div>;
// 4. Render data
return (
<>
<h1>Users</h1>
<ul>
{data?.map((user) => (
<li key={user.id}>
<strong>{user.name}</strong> — {user.email} — {user.phone}
</li>
))}
</ul>
</>
);
}
export default Profile;
3. Persistent & Browser Caching
For data that must survive page refreshes or be available offline.
-
LocalStorage / SessionStorage: Simple key-value stores for small, non-sensitive data (e.g., UI preferences, auth tokens).
-
IndexedDB: Best for large amounts of structured data or binary files (e.g., caching images locally).
-
Redux Persist: If you use Redux, this library automatically saves your store state to local storage and rehydrates it when the app reloads.
Q. In which lifecycle event do you make AJAX requests in React?
1. Functional Component (Modern Approach)
The useEffect hook is used to perform side effects like data fetching. By passing an empty array [] as the second argument, the fetch runs only once when the component mounts.
Example:
import { useState, useEffect } from 'react';
interface User {
id: number;
name: string;
}
function UserProfile() {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
// AJAX request starts here
fetch('https://jsonplaceholder.typicode.com/users/1')
.then(res => res.json())
.then(data => setUser(data)); // Updates state once data is received
}, []); // Run only on mount
return <div>{user ? user.name : "Loading..."}</div>;
}
export default UserProfile;
2. Class Component (Legacy Approach)
In a class component, you place the request in componentDidMount(). This ensures the component is already in the DOM before the network request starts.
Example:
import React, { Component } from 'react';
class UserProfile extends Component {
state = { user: null };
componentDidMount() {
// AJAX request starts here
fetch('https://api.example.com/user')
.then(res => res.json())
.then(data => this.setState({ user: data }));
}
render() {
return <div>{this.state.user ? this.state.user.name : "Loading..."}</div>;
}
}
⚝ Try this example on CodeSandbox
Q. How to use async await in React?
In React, you can use async/await to make your asynchronous code cleaner and more readable than standard promise chains. However, how you implement it differs slightly between functional and class components.
Functional Components (useEffect)
You cannot make the useEffect callback function itself async. This is because useEffect expects to return either nothing or a synchronous cleanup function, but an async function always returns a Promise.
To work around this, define an async function inside the effect and call it immediately.
Example:
import { useEffect, useState } from "react";
interface User {
id: number;
name: string;
}
export default function Users() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
let ignore = false;
// Define the async function
async function fetchUsers() {
try {
setLoading(true);
const res = await fetch("https://jsonplaceholder.typicode.com/users");
if (!res.ok) throw new Error("Request failed");
const data = await res.json();
if (!ignore) setUsers(data);
} catch (err) {
if (!ignore) setError(err instanceof Error ? err.message : "Unknown error");
} finally {
if (!ignore) setLoading(false);
}
}
fetchUsers();
return () => { ignore = true; };
}, []); // Empty array runs this only once on mount
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
2. Class Components (componentDidMount)
async componentDidMount() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
this.setState({ data });
} catch (error) {
console.error("Fetch failed", error);
}
}
⚝ Try this example on CodeSandbox
Q. How RxJS is used in React for state management?
RxJS is a library for reactive programming using Observables, to make it easier to compose asynchronous or callback-based code. Reactive programming is an event-based paradigm that allows us to run asynchronous sequences of events as soon as data is pushed to a consumer.
Core Concept:
- Keep shared state in an RxJS Subject (usually
BehaviorSubject). - Push updates with
next(). - Subscribe from React components to receive updates.
- Unsubscribe on unmount to avoid memory leaks.
RxJS Terminology:
-
Observable: The Stream. It represents a collection of future values or events. It doesn't do anything until someone listens to it.
-
Observer: The Listener. The consumer of an Observable. It provides handlers for next, error, and complete notifications.
-
Subscription: The Execution. This is the act of connecting an Observer to an Observable. It returns a “Subscription” object which you use to stop the flow.
-
Operators: The Filters/Tools. Pure functions that allow you to transform, filter, or combine streams of data (e.g.,
map,filter,debounceTime). -
Subject: The Multi-caster. A special type of Observable that can broadcast to many Observers at once. It is both an Observable (you can listen to it) and an Observer (you can feed it new data via
.next()). -
BehaviorSubject: A Stateful Subject. A Subject that stores the latest value and immediately sends that current value to new subscribers.
-
Pipe: The Assembly Line. A method used to chain Operators together in a readable sequence.
Example:
Here is a simple example of a Global Counter using a BehaviorSubject. This pattern allows any component to read or update the state without using Props or Context.
1. The Store (store.js)
We create a BehaviorSubject to hold the value and a simple function to update it.
import { BehaviorSubject } from 'rxjs';
// Initialize with 0
const counterSubject = new BehaviorSubject(0);
export const counterStore = {
// Observable for components to subscribe to
counter$: counterSubject.asObservable(),
// Method to update the state
increment: () => counterSubject.next(counterSubject.value + 1),
decrement: () => counterSubject.next(counterSubject.value - 1)
};
2. The React Component (Counter.js)
We use useEffect to “bridge” the RxJS stream into React's local state so the UI re-renders.
import React, { useState, useEffect } from 'react';
import { counterStore } from './store';
export function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
// Subscribe to changes
const subscription = counterStore.counter$.subscribe(setCount);
// CRITICAL: Clean up subscription on unmount
return () => subscription.unsubscribe();
}, []);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={counterStore.increment}>+</button>
<button onClick={counterStore.decrement}>-</button>
</div>
);
}
Note:
Use RxJS when you truly need stream composition; for simple app state, React context/useReducer may be simpler.
Reference:
# 14. REACT CONTEXT
Q. What is Context API in React?
The React Context API allows you to share data across the component tree without manually passing props at every level (avoiding “prop drilling”).
Core Concepts:
- React.createContext(): Creates a context object
<Context.Provider>: Wraps components and supplies the value- useContext(): Consumes the context value in any descendant
Common Use Cases:
- Theming: Switching between Light and Dark modes.
- User Authentication: Sharing the current logged-in user's info and login status.
- Localization: Managing language preferences (e.g., English vs. Spanish).
- Routing: Many libraries like React Router use Context internally to track the current URL
Example:
/**
* Counter Component
*/
const { useState, useContext } = React;
const CountContext = React.createContext();
function Counter() {
const { count, increase, decrease } = useContext(CountContext);
return (
<h2>
<button onClick={decrease}>Decrement</button>
<span className="count">{count}</span>
<button onClick={increase}>Increment</button>
</h2>
);
}
/**
* App Component
*/
function App() {
const [count, setCount] = useState(0);
const increase = () => {
setCount(count + 1);
};
const decrease = () => {
setCount(count - 1);
};
return (
<div>
<CountContext.Provider value=8>
<Counter />
</CountContext.Provider>
</div>
);
}
⚝ Try this example on CodeSandbox
Q. How do you solve performance corner cases while using context?
Performance issues in the React Context API typically stem from unnecessary re-renders — when the context value changes, every component that consumes that context (via useContext) will re-render, even if it only uses a piece of the data that didn't change.
Here are the primary ways to solve these performance corner cases:
1. Split Your Contexts
The most effective way to prevent unnecessary renders is to break a large, multi-purpose context into several smaller, highly specific ones.
- By Domain: Create separate contexts for different parts of your app (e.g., UserContext, ThemeContext, CartContext).
- By Update Frequency: Keep static or rarely-changing data (like a user's name) in a different context than high-frequency data (like a timer or scroll position).
2. Separate State and Dispatch
If you are using a useReducer or useState pattern, components that only need the “setter” (dispatch) function should not re-render when the “getter” (state) changes.
- The Pattern: Provide the state in one context and the dispatch function in another.
- Benefit: Buttons or controls that only trigger actions will never re-render just because the state they modify was updated.
3. Stabilize the Context Value
If you pass an object literal directly into the value prop (e.g., value=), React sees a “new” object on every render of the provider, triggering all consumers to update.
- Use useMemo: Wrap your value in the useMemo hook to ensure the object reference only changes when the actual dependencies change.
- Use useCallback: If you are passing functions, wrap them in useCallback to maintain a stable reference.
4. Optimize with Component Composition
You can “short-circuit” re-renders by moving the context consumption as far down the tree as possible.
- Pass as Children: Wrap the provider around a component that takes children. Since children is just a prop that hasn't changed, React will skip re-rendering the intermediate tree.
- Use React.memo: Wrap consumer components in
React.memoso they only re-render if their own props or the context they subscribe to actually changes.
5. Advanced: Selectors and External Stores
For massive applications where these methods aren't enough, consider these “pro” alternatives:
- Context Selectors: Use libraries like use-context-selector to subscribe only to specific “slices” of a context value.
- External State Management: If you have high-frequency updates (like mouse movements), React Context may not be the right tool. Libraries like Zustand or Jotai are specifically optimized to avoid the wholesale re-rendering issues of Context.
Example:
/**
* Counter Component
*
* Consumes CountContext to display the current count value
* and provides Increment/Decrement buttons to update it.
*/
import { createContext, useContext } from 'react';
interface CountContextType {
count: number;
increase: () => void;
decrease: () => void;
}
export const CountContext = createContext<CountContextType | undefined>(undefined);
function Counter() {
const { count, increase, decrease } = useContext(CountContext) as CountContextType;
return (
<h2>
<button onClick={decrease}>Decrement</button>
<span className="count"> {count} </span>
<button onClick={increase}>Increment</button>
</h2>
);
}
export default Counter;
/**
* App Component
*
* Root page component that manages the counter state and provides
* it via CountContext to the Counter child component.
* Uses useCallback to stabilize handlers and useMemo to avoid
* unnecessary context re-renders.
*/
import { useState, useMemo, useCallback } from 'react';
import Counter, { CountContext } from './child';
function App() {
const [count, setCount] = useState(0);
const increase = useCallback(() => setCount(prev => prev + 1), []);
const decrease = useCallback(() => setCount(prev => prev - 1), []);
const contextValue = useMemo(() => ({ count, increase, decrease }), [count, increase, decrease]);
return (
<div>
<CountContext.Provider value={contextValue}>
<Counter />
</CountContext.Provider>
</div>
);
}
export default App;
Q. What is the purpose of default value in context?
The defaultValue argument is only used when a component does not have a matching Provider above it in the tree. This can be helpful for testing components in isolation without wrapping them. Passing undefined as a Provider value does not cause consuming components to use defaultValue.
Purpose:
- It provides a fallback so consumers do not crash when no Provider is present.
- It makes components easier to test in isolation.
- It documents the expected shape/type of the context value.
Example:
/**
* Default value in Context API
*/
import { createContext, useContext } from "react";
const Context = createContext("Default Value");
/**
* Child1 Component
*/
function Child1() {
const context = useContext(Context);
return <h2>Child1: {context}</h2>;
}
/**
* Child2 Component
*/
function Child2() {
const context = useContext(Context);
return <h2>Child2: {context}</h2>;
}
/**
* App Component
*/
export default function App() {
return (
<>
<Context.Provider value={"Initial Value"}>
<Child1 /> {/* Child inside Provider will get "Initial Value" */}
</Context.Provider>
<Child2 /> {/* Child outside Provider will get "Default Value" */}
</>
);
}
⚝ Try this example on CodeSandbox
Q. How to use contextType react?
The ContextType property on a class component can be assigned a Context object created by React.createContext() method. This property lets you consume the nearest current value of the context using this.context. We can access this.context in any lifecycle method including the render functions.
Key Concept:
- Create a context with
React.createContext(defaultValue). - Wrap part of the tree with Provider and pass value.
- In a class component, assign static
contextType = MyContext. - Access context anywhere in the class with
this.context(render and lifecycle methods).
Example:
/**
* ContextType Example
*
* Demonstrates class-based context consumption using static contextType.
* ThemeContext provides a "dark" theme value to the Header component,
* which reads it via this.context.
*/
import React from "react";
const ThemeContext = React.createContext("light");
class Header extends React.Component {
static readonly contextType = ThemeContext;
componentDidMount() {
console.log("Current theme:", this.context);
}
render() {
const theme = this.context as string;
return <h1>Theme: {theme}</h1>;
}
}
export default function App() {
return (
<ThemeContext.Provider value="dark">
<Header />
</ThemeContext.Provider>
);
}
⚝ Try this example on CodeSandbox
Q. How to update React Context from inside a child component?
The Context API allows data storage and makes it accessible to any child component who want to use it. This is valid whatever level of component graph the children is in.
Example:
/**
* React Context API
*/
import { useState, useContext, createContext, useMemo, useCallback } from "react";
interface MyContextType {
count: number;
increment: () => void;
}
const MyContext = createContext<MyContextType | undefined>(undefined);
/**
* Child Component
*/
const ChildComponent = () => {
const { count, increment } = useContext(MyContext) as MyContextType;
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
/**
* App Component
*
* Root component that manages count state and provides it via MyContext.
* Uses useMemo to memoize the context value and useCallback to stabilize
* the increment handler, preventing unnecessary re-renders in consumers.
*/
export default function App() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(prev => prev + 1);
}, []);
const contextValue = useMemo(() => ({ count, increment }), [count, increment]);
return (
<MyContext.Provider value={contextValue}>
<ChildComponent />
</MyContext.Provider>
);
}
Here, the state is managed in the parent component alongside an updater function that modifies it. Both the state value and the updater function are passed as the context value via the Provider. Any child component can then consume this context, call the updater function, and trigger a state change in the parent — effectively updating the shared context.
⚝ Try this example on CodeSandbox
Q. What is prop drilling and how can you avoid it?
Prop drilling is when data is passed through multiple intermediate component layers via props just to reach a deeply nested child that needs it — even though the components in between don't use that data themselves.
Why it's a problem:
- Maintenance: Changing the data structure requires updating every component in the chain.
- Complexity: It makes components harder to reuse because they are tightly coupled to the props they pass down.
- Readability: It becomes difficult to track where data is coming from in large trees.
Using Context API:
The Context API solves some of these prop drilling problems. It let pass data to all of the components in the tree without writing them manually in each of them. Shared data can be anything: state, functions, objects, we name it, and it is accessible to all nested levels that are in the scope of the context.
Example:
/**
* React Context API
*
* Demonstrates sharing a value across the component tree without prop drilling.
* NumberContext provides a numeric value to all descendant consumers.
*/
import { useContext, createContext } from "react";
// It returns an object with 2 values:
// { Provider, Consumer }
const NumberContext = createContext<number>(0);
const CONTEXT_VALUE = 100;
function Display() {
const value = useContext(NumberContext);
return <h3>Context Value: {value}</h3>;
}
/**
* App Component
*
* Wraps the component tree with NumberContext.Provider,
* making CONTEXT_VALUE available to all children via useContext.
*/
export default function App() {
return (
<NumberContext.Provider value={CONTEXT_VALUE}>
<Display />
</NumberContext.Provider>
);
}
⚝ Try this example on CodeSandbox
Q. What is Zustand and how does it compare to Redux?
Zustand is a small, fast, and flexible state management library for React. It uses a simplified store API without reducers, actions, or boilerplate.
Zustand store:
import { create } from 'zustand';
const useCounterStore = create((set, get) => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
// Access current state in an action using get()
double: () => set({ count: get().count * 2 }),
}));
Using Zustand in components:
function Counter() {
// Only subscribes to the pieces of state it uses — no unnecessary re-renders
const count = useCounterStore(state => state.count);
const increment = useCounterStore(state => state.increment);
return (
<div>
<p>{count}</p>
<button onClick={increment}>+</button>
</div>
);
}
// ✅ Can also use outside of React (in utilities, non-component code)
const currentCount = useCounterStore.getState().count;
useCounterStore.setState({ count: 10 });
Async actions in Zustand:
const useUserStore = create((set) => ({
user: null,
loading: false,
fetchUser: async (id) => {
set({ loading: true });
const user = await fetch(`/api/users/${id}`).then(r => r.json());
set({ user, loading: false });
},
}));
Zustand vs Redux comparison:
| Feature | Zustand | Redux Toolkit |
|---|---|---|
| Bundle size | ~1 KB | ~40 KB |
| Boilerplate | Minimal (no actions/reducers) | Low (with RTK), but more structured |
| Learning curve | Very easy | Medium |
| DevTools | Via middleware | Excellent (Redux DevTools) |
| Middleware | Simple (persist, devtools) |
Extensive ecosystem |
| Outside React | ✅ getState() / setState() |
✅ store.getState() |
| TypeScript | Excellent | Excellent |
| Use case | Small-medium apps, simple global state | Large apps needing strict patterns |
When to choose Zustand over Redux:
- Smaller applications or features that don't need strict action-based patterns.
- You want minimal setup with no Provider wrapping needed.
- You need to read/write state outside of React components.
- Performance is critical — Zustand uses selective subscriptions by default.
# 15. REACT COMPOSITION
Q. Explain Inheritance in React?
In React, inheritance is a concept where one component inherits properties or behaviors from another, typically using the extends keyword in Class Components. However, it is considered an anti-pattern and is officially discouraged by the React team. The React team recommends Composition over Inheritance.
Example:
/**
* Parent Class
*/
import React from "react";
export class ParentClass extends React.Component {
constructor(props) {
super(props);
this.callMe = this.callMe.bind(this);
}
// ParentClass function
callMe() {
console.log("This is a method from parent class");
}
render() {
return false;
}
}
/**
* Child Class
*/
export default class App extends ParentClass {
render() {
return <button onClick={() => this.callMe()}>Call Parent</button>;
}
}
⚝ Try this example on CodeSandbox
Note:
React does not use inheritance except in the initial component class, which extends from the react package.
Q. Explain Composition in React?
Composition is a pattern where components are built by combining smaller, reusable components together — rather than inheriting from a parent. It follows the “has-a” relationship (a component uses another component).
Benefits
-
Solves “Prop Drilling”: By composing components at a higher level, you can pass data directly to the deep child that needs it, rather than passing it through five intermediate layers.
-
Avoids “Prop Hell”: Instead of one massive component with 20 props to toggle every feature, you break it into small pieces (e.g., Alert.Title, Alert.Icon, Alert.Body) that the user can mix and match.
-
Performance: Composition can reduce unnecessary re-renders. When a parent's state changes, React only re-renders the parts that actually changed; wrapped children often stay the same and are skipped.
-
Flexibility: You can change the internal structure of a child component without worrying about breaking a parent class's internal logic
Example: Basic Containment using children prop
/**
* Composition in React
*/
// Card.jsx (a reusable wrapper component)
const Card = ({ children }) => (
<div style=>
{children}
</div>
);
// App.jsx (composing content inside the Card)
function App() {
return (
<Card>
<h2>Card Title</h2>
<p>This is some content inside the card.</p>
</Card>
);
}
export default App;
⚝ Try this example on CodeSandbox
Q. How Composition is different from Inheritance in React?
Inheritance used the is-a relationship method. Derived components had to inherit the properties of the base component and it was quite complicated while modifying the behavior of any component.
Composition does not inherit properties, only the behavior. In inheritance, it was difficult to add new behavior because the derived component was inheriting all the properties of parent class and it was quite difficult to add new behavior. But in composition, we only inherit behavior and adding new behavior is fairly simple and easy.
React recommends use of Composition over Inheritance, here is why. Everything in React is a component, and it follows a strong component based model. This is one of the primary reasons that composition is a better approach than inheritance for code reuse.
Composition vs Inheritance
| Inheritance | Composition | |
|---|---|---|
| Relationship | is-a | has-a |
| Coupling | Tightly coupled | Loosely coupled |
| Behavior reuse | Inherits all parent properties | Shares only needed behavior |
| Flexibility | Rigid — hard to modify | Flexible — easy to extend |
| React recommendation | Anti-pattern | Preferred |
# 16. PURE COMPONENTS
Q. What are Pure Components in React?
Pure Components in React are the components which do not re-renders when the value of state and props has been updated with the same values. Pure Components restricts the re-rendering ensuring the higher performance of the Component.
Features of React Pure Components:
- Prevents re-rendering of Component if props or state is the same
- Takes care of
shouldComponentUpdate()implicitly State()andPropsare Shallow Compared- Pure Components are more performant in certain cases
Example:
/**
* React Pure Component
*/
export default class App extends React.PureComponent {
constructor() {
super();
this.state = {
userArray: [1, 2, 3, 4, 5]
};
// Here we are creating the new Array Object during setState using "Spread" Operator
setInterval(() => {
this.setState({
userArray: [...this.state.userArray, 6]
});
}, 1000);
}
render() {
return <b>Array Length is: {this.state.userArray.length}</b>;
}
}
⚝ Try this example on CodeSandbox
Q. What is difference between Pure Component vs Component?
PureComponent is exactly the same as Component except that it handles the shouldComponentUpdate() method. The major difference between React.PureComponent and React.Component is PureComponent does a shallow comparison on state change. It means that when comparing scalar values it compares their values, but when comparing objects it compares only references. It helps to improve the performance of the app.
A component rerenders every time its parent rerenders, regardless of whether the component's props and state have changed.
On the other hand, a pure component will not rerender if its parent rerenders, unless the pure component's props (or state) have changed.
When to use React.PureComponent:
- State/Props should be an immutable object
- State/Props should not have a hierarchy
- We should call forceUpdate when data changes
Example:
// Regular class component
class App extends React.Component {
render() {
console.log('Regular component rendered');
return <h1>Component Example !</h1>
}
}
// React Pure class component
class Message extends React.PureComponent {
render() {
console.log('Pure component rendered');
return <h1>PureComponent Example !</h1>
}
}
Q. What are the problems of using render props with PureComponent?
If you create a function inside a render method, it negates the purpose of pure component. Because the shallow prop comparison will always return false for new props, and each render in this case will generate a new value for the render prop. You can solve this issue by defining the render function as instance method.
Example:
class Mouse extends React.PureComponent {
// Mouse Component...
}
class MouseTracker extends React.Component {
// Defined as an instance method, `this.renderTheCat` always
// refers to *same* function when we use it in render
renderTheCat(mouse) {
return <Cat mouse={mouse} />;
}
render() {
return (
<div>
<h1>Move the mouse around!</h1>
{/* define the render function as instance method */}
<Mouse render={this.renderTheCat} />
</div>
);
}
}
Q. When to use PureComponent over Component?
In React class components, use PureComponent over Component to optimize performance by preventing unnecessary re-renders. While Component re-renders every time its parent does, PureComponent only re-renders if its props or state actually change based on a shallow comparison.
When to Use PureComponent
- Performance Bottlenecks: When a component is expensive to render (e.g., contains complex calculations or many children) and you want to skip updates if the data is identical.
- Predictable Props/State: When your data consists of primitive types (strings, numbers, booleans) or stable object references.
- Large Lists: It is highly effective for individual items in large lists or tables where many items remain unchanged while the parent updates.
- Immutable Data: If you use libraries like Immutable.js or always create new object references for updates, PureComponent will accurately detect changes.
When to Stick with Component
- Deeply Nested Data: PureComponent only does a shallow check. If you mutate a property inside a nested object without changing the top-level reference, the component will not re-render, leading to stale UI.
- Custom Update Logic: If you need to manually control when a component updates (e.g., only when specific props change), you must use Component and implement the shouldComponentUpdate lifecycle method.
- Frequent Changes: If the props/state change on every render anyway, the extra “shallow comparison” step becomes unnecessary overhead that can slightly slow down your app.
Example:
// ✅ Good use of PureComponent
class UserCard extends React.PureComponent {
render() {
const { name, age, email } = this.props; // Simple props
return <div>{name} - {age} - {email}</div>;
}
}
// ❌ Bad use of PureComponent
class UserProfile extends React.PureComponent {
render() {
const { user } = this.props; // Complex nested object
return <div>{user.profile.settings.theme}</div>;
}
}
Modern Alternative
React now recommends defining components as functions instead of classes. To get the performance benefits of PureComponent in a functional component, wrap it in React.memo().
# 17. HIGHER ORDER COMPONENTS
Q. What are Higher Order Components in React.js?
Higher Order Components (HOCs) are advanced React patterns where a function takes a component and returns a new enhanced component with additional props or functionality.
Higher-Order Components are not part of the React API. They are the pattern that emerges from React's compositional nature. The component transforms props into UI, and a higher-order component converts a component into another component. The examples of HOCs are Redux's connect and Relay's createContainer.
/**
* HOC that adds loading functionality
*/
function withLoading(Component) {
return function WithLoadingComponent({ isLoading, ...props }) {
if (isLoading) {
return <div>Loading...</div>;
}
return <Component {...props} />;
};
}
// Usage
const UserList = ({ users }) => (
<ul>{users.map(user => <li key={user.id}>{user.name}</li>)}</ul>
);
const UserListWithLoading = withLoading(UserList);
// Render with loading state
<UserListWithLoading isLoading={true} users={[]} />
Note:
- A HOC does not modify or mutate components. It creates a new one.
- A HOC is used to compose components for code reuse.
- A HOC is a pure function. It has no side effects, returning only a new component.
Modern alternatives:
HOCs are still valid, but Hooks are often preferred for most use cases:
// HOC approach
const UserProfile = withAuth(withData(Component));
// Hooks approach (cleaner)
function UserProfile() {
const { isAuthenticated, user } = useAuth();
const { data, loading } = useData('/api/user');
if (!isAuthenticated) return <Redirect to="/login" />;
if (loading) return <Spinner />;
return <div>{data.name}</div>;
}
⚝ Try this example on CodeSandbox
Q. What are the benefits of using HOC?
Benefits:
-
Code Reusability - HOCs allow you to extract common logic used by many components into a single, reusable function. This prevents code duplication (Don't Repeat Yourself) and makes updates significantly easier because changes to the shared logic only need to be made in one place
-
No Method Name Clashing - If two HOCs implement the same method, there is no name collision
-
Single Responsibility Principle - Easy to make small, reusable units of code that follow SRP
-
Composability - HOCs can be nested or “composed” together to add multiple layers of functionality to a single component. Because they are pure functions that return components, they are easy to chain using utility functions like compose (often found in libraries like Redux or Lodash).
Problems:
- Boilerplate code needed (like setting
displayNamefor debugging) - Must ensure all relevant props are passed through to the component
- Need to hoist static methods from the wrapped component
- Composing several HOCs can create a deeply nested tree that's difficult to debug
Q. What are Higher Order Component factory implementations?
Creating a higher order component basically involves manipulating WrappedComponent which can be done in two ways:
- Props Proxy (PP)
- Inheritance Inversion (II)
Both enable different ways of manipulating the WrappedComponent.
1. Props Proxy:
In this approach, The HOC creates a container component that wraps the WrappedComponent. It intercepts incoming props, adds or modifies them, and then passes them down.
Example:
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
render() {
return <WrappedComponent {...this.props}/>
}
}
}
Props Proxy can be implemented via a number of ways
- Manipulating props
- Accessing the instance via Refs
- Abstracting State
- Wrapping the WrappedComponent with other elements
2. Inheritance Inversion:
Inheritance Inversion allows the HOC to have access to the WrappedComponent instance via this keyword, which means it has access to the state, props, component lifecycle hooks and the render method.
Example:
function iiHOC(WrappedComponent) {
return class Enhancer extends WrappedComponent {
render() {
return super.render()
}
}
}
Inheritance Inversion can be used in:
- Conditional Rendering (Render Highjacking)
- State Manipulation
The key difference is that Props Proxy wraps the component from the outside, while Inheritance Inversion extends the component directly, giving deeper access to its internals.
Differences
| Feature | Props Proxy (PP) | Inheritance Inversion (II) |
|---|---|---|
| Primary Method | Composes via wrapping | Composes via inheritance |
| Logic Type | Adding/modifying props | Manipulating render/state |
| Component Support | Class & Functional | Class only |
| Complexity | Simple & Readable | Advanced & Powerful |
Q. Explain Inheritance Inversion (iiHOC) in react?
Inheritance Inversion (iiHOC) is a powerful but rare implementation of Higher-Order Components where the returned component extends the WrappedComponent instead of wrapping it.
It is called “Inversion” because, in standard object-oriented programming, a specialized component would extend a base class to get more features. Here, the HOC (the enhancer) is the one doing the extending, giving it “passive” control over the original component.
Inheritance Inversion gives the HOC access to the WrappedComponent instance via this, which means we can use the state, props, component lifecycle and even the render method.
Example:
/**
* Inheritance Inversion
*/
class Welcome extends React.Component {
render() {
return (
<div> Welcome {his.props.user}</div>
)
}
}
const withUser = (WrappedComponent) => {
return class extends React.Component {
render() {
if(this.props.user) {
return (
<WrappedComponent {...this.props} />
)
}
return <div>Welcome Guest!</div>
}
}
}
const withLoader = (WrappedComponent) => {
return class extends WrappedComponent {
render() {
const { isLoader } = this.props
if(!isLoaded) {
return <div>Loading...</div>
}
return super.render()
}
}
}
export default withLoader(withUser(Welcome))
Q. How to create props proxy for Higher Order Component component?
To create a props proxy for a Higher Order Component (HOC) in React, you wrap a component and pass all received props to the WrappedComponent. This allows the HOC to intercept, modify, or add props before passing them down.
Example:
function withPropsProxy(WrappedComponent) {
return function PropsProxyComponent(props) {
// You can modify or add props here if needed
return <WrappedComponent {...props} />;
};
}
Usage:
const EnhancedComponent = withPropsProxy(MyComponent);
// <EnhancedComponent someProp="value" /> will pass someProp to MyComponent
This pattern is called a “props proxy” because the HOC acts as a proxy for the props, allowing you to manipulate them as needed before rendering the wrapped component.
Props Proxy HOCs are useful to the following situations:
- Manipulating props
- Accessing the instance via Refs (be careful, avoid using refs)
- Abstracting State
- Wrapping/Composing the WrappedComponent with other elements
Q. What is the purpose of displayName class property?
By default, React infers the displayName from the function or class name, but it can set it explicitly—especially useful for higher-order components (HOCs) that wrap other components, so the debug output reflects the wrapped component's name.
It helps tools like React DevTools and error messages show a clear, descriptive name for the component, making it easier to identify in the component tree.
Example:
function withSubscription(WrappedComponent) {
class WithSubscription extends React.Component {/* ... */}
WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
return WithSubscription;
}
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
# 18. REACT REFS
Q. What do you understand by refs in React?
The Refs (short for “references”) provide a way to access DOM without triggering a component re-render. React Refs are a useful feature that act as a means to reference a DOM element or a class component from within a parent component.
Refs also provide some flexibility for referencing elements within a child component from a parent component, in the form of ref forwarding.
Example:
/**
* Refs
*/
import { useRef, useState } from "react";
function App() {
// create a ref to store the textInput DOM element
const textInput = useRef<HTMLInputElement>(null);
const [value, setValue] = useState('');
// Set the state for the ref
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
setValue(textInput.current?.value ?? '');
};
return (
<div>
<h1>React Ref - useRef Example</h1>
<h3>Value: {value}</h3>
<form onSubmit={handleSubmit}>
{/** Call the ref on <input> so we can use it to update the <h3> value **/}
<input type="text" ref={textInput} />
<button>Submit</button>
</form>
</div>
);
}
export default App;
When to Use Refs:
- Managing focus, text selection, or media playback.
- Triggering imperative animations.
- Integrating non-React libraries (like D3.js or Google Maps) that require direct access to DOM nodes.
- Keeping track of
setTimeoutorsetIntervalIDs so they can be cleared later.
When not to use refs:
- Should not be used with functional components (they don't have instances).
- Not to be used on things that can be done declaratively.
Q. How can I use multiple refs for an array of elements with hooks?
To manage multiple refs for an array of elements, use a single useRef initialized to an empty array, then populate it via callback refs in your .map():
Example:
/**
* Multiple Refs
*/
import { useRef } from 'react';
function MyComponent() {
const items = [10, 20, 30];
const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
return (
<ul>
{items.map((item, index) => (
<li
key={item}
ref={el => { itemRefs.current[index] = el; }}
>
{item}
</li>
))}
</ul>
);
}
export default MyComponent;
⚝ Try this example on CodeSandbox
Q. What is the difference between useRef() and createRef()?
1. useRef():
The useRef is a hook that uses the same ref throughout. It saves its value between re-renders in a functional component and doesn't create a new instance of the ref for every re-render. It persists the existing ref between re-renders.
Example:
/**
* useRef()
*/
import { useEffect, useRef, useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
const ref = useRef<number | null>(null);
useEffect(() => {
ref.current = 100; // Initial Value
}, []);
useEffect(() => {
console.log(count, ref.current);
}, [count]);
return (
<div className="App">
<button onClick={() => setCount((c) => c + 10)}>Increment</button>
<p>{count}</p>
</div>
);
}
⚝ Try this example on CodeSandbox
2. createRef():
The createRef is a function that creates a new ref every time. Unlike the useRef, it does not save its value between re-renders, instead creates a new instance of the ref for every re-render. Thus implying that it does not persist the existing ref between re-renders.
Example:
/**
* createRef()
*/
import React, { createRef } from "react";
type AppProps = Record<string, never>;
type AppState = { count: number };
export default class App extends React.Component<AppProps, AppState> {
ref = createRef<number>();
constructor(props: AppProps) {
super(props);
this.state = { count: 0 };
}
componentDidMount() {
this.ref.current = 100; // Initial Value
}
componentDidUpdate(prevProps: AppProps, prevState: AppState) {
if (prevState.count !== this.state.count) {
console.log(this.state.count, this.ref.current);
}
}
render() {
return (
<div className="App">
<button onClick={() => this.setState((s) => ({ count: s.count + 10 }))}>
Increment
</button>
<p>{this.state.count}</p>
</div>
);
}
}
Note:
Always use useRef() in functional components. createRef() is intended for class components where it's called once in the constructor. In classes, you typically call it once in the constructor and assign it to this.myRef. Because the class instance stays the same during re-renders, the ref remains stable.
⚝ Try this example on CodeSandbox
Q. Why are inline ref callback or function not recommended?
Inline ref callbacks are not recommended because they get called twice on every update:
- Cleanup: It calls the old function with null to clear the previous reference.
- Setup: It calls the new function with the actual DOM element to set the new reference.
This happens because a new function instance is created on every render, so React treats it as a different ref each time and must clean up the old one before assigning the new one.
Example: The Problem: Inline Ref Callbacks
/**
* Inline Ref Callback()
*/
import { useState } from 'react';
function InlineRefExample() {
const [count, setCount] = useState(0);
return (
<div>
<input
ref={(node) => {
// This logs TWICE on every re-render:
// 1. "Ref is null" (cleanup)
// 2. "Ref is [object HTMLInputElement]" (setup)
console.log("Ref is:", node);
}}
/>
<button onClick={() => setCount(c => c + 1)}>Re-render ({count})</button>
</div>
);
}
Example: The Solution: useCallback Ref
import { useState, useCallback } from 'react';
function StableRefExample() {
const [count, setCount] = useState(0);
// Memoize the function so it does not change on re-renders
const stableRef = useCallback((node) => {
if (node !== null) {
console.log("Ref initialized once:", node);
}
}, []); // Empty deps means it never changes
return (
<div>
<input ref={stableRef} />
<button onClick={() => setCount(c => c + 1)}>Re-render ({count})</button>
</div>
);
}
⚝ Try this example on CodeSandbox
Q. Which is the preferred option callback refs or findDOMNode()?
Callback refs are the preferred option over findDOMNode() in React.
While findDOMNode() was once a common way to access an element's underlying DOM node, it is now discouraged and deprecated in Strict Mode. In modern React, you should use refs (either via useRef for functional components or createRef/callback refs for class components)
Example: The legacy approach of using findDOMNode()
class MyComponent extends Component {
componentDidMount() {
findDOMNode(this).scrollIntoView()
}
render() {
return <div />
}
}
Example: The recommended approach is useRef
import { useRef, useEffect } from 'react';
function MyComponent() {
const nodeRef = useRef(null);
useEffect(() => {
nodeRef.current.scrollIntoView();
}, []);
return <div ref={nodeRef} />;
}
Q. How to set focus on an input field after rendering?
Use useRef to get a reference to the input element and useEffect with an empty dependency array to call .focus() once after the component mounts.
Example:
import { useRef, useEffect } from 'react';
function AutoFocusTextInput() {
const textInput = useRef(null);
useEffect(() => {
textInput.current.focus();
}, []);
return <input ref={textInput} />;
}
Q. Why are string refs considered legacy in React?
Although string refs are not deprecated, they are considered legacy, and will likely be deprecated at some point in the future. Callback refs are preferred.
Callback Refs:
Instead of passing a ref attribute created by createRef(), you pass a function. The function receives the React component instance or HTML DOM element as its argument, which can be stored and accessed elsewhere.
Example: Legacy String Ref (Deprecated)
// LEGACY: Do not use this in modern React
class LegacyComponent extends React.Component {
componentDidMount() {
// Accessing the element via the string name
this.refs.myInput.focus();
}
render() {
return <input ref="myInput" />;
}
}
Example: Modern useRef Hook (Recommended)
// MODERN: The standard approach
import { useRef } from 'react';
function ModernComponent() {
const inputRef = useRef(null);
const handleClick = () => {
// Accessing the DOM element via .current
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} />
<button onClick={handleClick}>Focus Input</button>
</>
);
}
Example: Modern Callback Ref
// ADVANCED: Useful for dynamic elements
function CallbackRefExample() {
const setRef = (node) => {
if (node) {
console.log("Input is mounted:", node);
node.focus();
} else {
console.log("Input is unmounted");
}
};
return <input ref={setRef} />;
}
⚝ Try this example on CodeSandbox
Q. What is forwardRef() in React?
Ref forwarding is a technique for passing a ref through a component to one of its children. It is very useful for cases like reusable component libraries and Higher Order Components (HOC).
We can forward a ref to a component by using the React.forwardRef() function. Ref forwarding allows components to take a ref they receive and pass it further down (in other words, “forward” it) to a child.
Example:
// Child component wraps itself with forwardRef
const TextInput = React.forwardRef((props, ref) => (
<input type="text" ref={ref} />
));
// Parent creates a ref and passes it to the child
function Parent() {
const inputRef = React.createRef();
const handleClick = () => {
inputRef.current.focus(); // directly accesses the <input> DOM node
};
return (
<>
<TextInput ref={inputRef} />
<button onClick={handleClick}>Focus</button>
</>
);
}
In the example above, we have a component called TextInput that has a child which is an input field. First, we start by creating a ref with the line of code below:
const inputRef = React.createRef()
We pass our ref down to <TextInput ref={inputRef}> by specifying it as a JSX attribute. React then forwards the ref to the forwardRef() function as a second argument. Next, We forward this ref argument down to <input ref={ref}>. The value of the DOM node can now be accessed at inputRef.current.
Common use cases:
- Reusable component libraries (e.g.,
<Input>,<Button>) that need to expose DOM nodes - Higher Order Components (HOCs) that wrap components but must pass refs through
- Integrating with third-party libraries that require a direct DOM reference.
Q. How to debug forwardRefs() in DevTools?
React.forwardRef accepts a render function as parameter and DevTools uses this function to determine what to display for the ref forwarding component.
Problem: If you don't name the render function or not using displayName property then it will appear as “ForwardRef” in the DevTools,
const WrappedComponent = React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
Solution: If you name the render function then it will appear as “ForwardRef(myFunction)”
/**
* set displayName explicitly
*/
const WrappedComponent = React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
WrappedComponent.displayName = 'myFunction';
// DevTools shows: ForwardRef(myFunction)
Example:
const ForwardP = React.forwardRef(function ForwardP(props, ref) {
return (
<>
<p>I'm a real component too</p>
<p>
Especially with <code>useImperativeMethods</code>
</p>
<p {...props} ref={ref} />
</>
);
});
function App() {
return (
<div className="App">
<ForwardP style=>
But my props are <code>null</code> in DevTools
</ForwardP>
</div>
);
}
⚝ Try this example on CodeSandbox
Q. What is difference between useRef() and useState() in React?
Both useRef() and useState() are React hooks for managing data, but they serve different purposes and behave differently. useState triggers a re-render when updated; useRef does not.
Key Differences:
| Feature | useRef() | useState() |
|---|---|---|
| Purpose | Store mutable values that persist | Manage component state |
| Re-render | Does NOT trigger re-render | Triggers re-render |
| Update | Synchronous | Asynchronous |
| Access | .current property | Direct value |
| Common Use | DOM references, timers | UI state, user input |
useState(): for values that drive the UI
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1); // Triggers re-render
console.log(count); // May show old value (async)
};
return (
<div>
<h2>Count: {count}</h2>
<button onClick={handleClick}>Increment</button>
</div>
);
}
useRef(): for values that don't need to update the UI
import React, { useRef } from 'react';
function Counter() {
const countRef = useRef(0);
const handleClick = () => {
countRef.current += 1; // Does NOT trigger re-render
console.log(countRef.current); // Shows updated value immediately
};
return (
<div>
<h2>Count: {countRef.current}</h2> {/* Won\'t update on screen */}
<button onClick={handleClick}>Increment</button>
</div>
);
}
Common Use Cases:
1. useRef for DOM Access:
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
2. useRef for Storing Previous Values:
function Component() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
}, [count]);
return (
<div>
<h2>Current: {count}</h2>
<h2>Previous: {prevCountRef.current}</h2>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
3. useRef for Timers:
function Timer() {
const [seconds, setSeconds] = useState(0);
const intervalRef = useRef(null);
const startTimer = () => {
intervalRef.current = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
};
const stopTimer = () => {
clearInterval(intervalRef.current);
};
return (
<div>
<h2>{seconds} seconds</h2>
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Stop</button>
</div>
);
}
Use useState when:
- Value changes should update the UI
- Managing form inputs
- Toggling UI elements
- Any data that affects rendering
Use useRef when:
- Accessing DOM elements
- Storing mutable values that do not affect rendering
- Keeping track of previous values
- Storing timer IDs
- Avoiding unnecessary re-renders
Note:
- useRef updates are synchronous, useState updates are asynchronous
- useRef doesn't notify about changes, useState triggers re-render
- Both persist values across re-renders
# 19. REACT ERROR BOUNDARIES
Q. What are error boundaries in React?
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.
Key methods:
static getDerivedStateFromError()Update state to render fallback UIcomponentDidCatch()Log error info to a reporting servicerender()Render children or fallback UI
Syntax:
<ErrorBoundary>
<User/>
</ErrorBoundary>
Example:
/**
* ErrorBoundary Component
*
* Note: Error boundaries only catch errors during rendering, in lifecycle
* methods, and in constructors of the whole tree below them. They do NOT
* catch errors inside event handlers.
*/
import React from "react";
class ErrorBoundary extends React.Component<
{ children: React.ReactNode },
{ hasError: boolean }
> {
constructor(props: { children: React.ReactNode }) {
super(props);
this.state = { hasError: false };
}
// Updates state so the next render shows the fallback UI
static getDerivedStateFromError() {
return { hasError: true };
}
// Used for logging error information
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
function MyComponent() {
return <p>Hello from MyComponent</p>;
}
// Usage: Wrap components that might crash during render
export default function App() {
return (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
}
Here, We have a state object having two variables isErrorOccured and errorMessage which will be updated to true if any error occurs.
⚝ Try this example on CodeSandbox
Q. What is the difference between try catch block and error boundaries?
1. Try…catch is used in specific code blocks where you program the functionality of the application.
/**
* Try…catch
*/
import { useState } from 'react';
function Profile() {
const [error, setError] = useState<string | null>(null);
const handleUpdate = async () => {
try {
// Imperative logic: Step-by-step actions
const response = await fetch('/api/update-profile');
if (!response.ok) throw new Error('Update failed');
// ... process data
} catch (err) {
// Manually handle the error state for the UI
setError(err instanceof Error ? err.message : 'An unknown error occurred');
}
};
return (
<div>
{error && <p>Error: {error}!</p>}
<button onClick={handleUpdate}>Update Profile</button>
</div>
);
}
export default Profile;
2. Error Boundaries deal with declarative code. Imperative programming is how you do something and declarative programming is what you do.
With error boundary, if there is an error, you can trigger a fallback UI; whereas, with try…catch, you can catch errors in your code.
import ErrorBoundary from "error-boundary";
function Users() {
return (
<div>
<ErrorBoundary>
<Users />
</ErrorBoundary>
</div>
)
}
Differences:
In React, the primary difference is that Error Boundaries are for declarative code (the UI structure), while try-catch is for imperative code (logical steps or actions). If there's an error, Error Boundaries trigger a fallback UI for the entire component subtree, whereas try...catch catches errors within specific code execution paths.
Note:
Error Boundaries cannot catch errors in event handlers — you must use try...catch for those.
⚝ Try this example on CodeSandbox
Q. What is the benefit of component stack trace from error boundary?
Component Stack Trace prints all errors that occurred during rendering to the console in development, even if the application accidentally swallows them. It also display the filenames and line numbers in the component stack trace.
Example:
Q. What are the methods invoked during error handling?
To create an error boundary, we simply have to create a class component and define a state variable for determining whether the error boundary has caught an error. Our class component should also have at least three methods:
- A static method called getDerivedStateFromError(), which is used to update the error boundary's state
- A componentDidCatch() lifecycle method for performing operations when our error boundaries catch an error, such as logging to an error logging service
- A render() method for rendering our error boundary's child or the fallback UI in case of an error
Example:
/**
* Error Boundary in React
*/
import React from 'react';
class ErrorBoundary extends React.Component<
{ children: React.ReactNode },
{ hasError: boolean }
> {
constructor(props: { children: React.ReactNode }) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(): { hasError: boolean } {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
// Log the error to an error reporting service
console.error(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;
# 20. LAZY LOADING
Q. How to set up lazy loading components in React?
1. REACT.LAZY():
React.lazy is a function that lets you load components lazily through what is called code splitting without help from any external libraries. It makes possible for us to dynamically import components but they are rendered like regular components. This means that the bundle containing the component will only be loaded when the component is rendered.
React.lazy() takes a function that returns a promise as it's argument, the function returns a promise by calling import() to load the content. The returned Promise resolves to a module with a default containing the React Component.
Example: Create a lazy wrapper for your component
// Without Lazy
import MyComponent from './MyComponent';
// With Lazy
const MyComponent = React.lazy(() => import('./MyComponent'));
2. SUSPENSE:
React.Suspense is a component that can be used to wrap lazy components. A React.Suspense takes a fallback prop that can be any react element, it renders this prop as a placeholder to deliver a smooth experience and also give user feedback while the lazy component is being loaded.
Example: Use a suspense boundary with a custom loading indicator:
/**
* Suspense
*/
import React, { Suspense } from 'react';
const MyComponent = React.lazy(() => import('./MyComponent'));
const App = () => {
return (
<div>
<Suspense fallback={<div>Loading ... </div>}>
<MyComponent />
</Suspense>
</div>
);
}
Example: For navigation, lazy load each page and wrap the navigation area:
/**
* React Lazy Loading Routes
*/
import { lazy, Suspense } from 'react';
import { Routes, Route, Link } from 'react-router-dom';
const Dashboard = lazy(() => import('./Dashboard'));
const Profile = lazy(() => import('./Profile'));
function Navigation() {
return (
<Suspense fallback={<span>Loading page...</span>}>
<nav>
<Link to="/">Dashboard</Link>
<Link to="/profile">Profile</Link>
</nav>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Suspense>
);
}
This method ensures components are loaded only when needed, improving performance and user experience.
⚝ Try this example on CodeSandbox
# 21. REACT INTERNATIONALIZATION
Q. How to translate your React app with react-i18next?
1. Install Dependencies:
npm install i18next react-i18next i18next-browser-languagedetector i18next-http-backend
i18next-browser-languagedetector: Automatically detects the user's browser language.i18next-http-backend: Allows you to load translation files from your/publicfolder rather than bundling them into your JavaScript.
2. Configure i18next:
Create a new file i18n.js beside your index.js containing following content:
/**
* i18next Component
*/
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import HttpBackend from 'i18next-http-backend';
i18n
.use(HttpBackend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false, // React already escapes by default
},
backend: {
loadPath: '/locales//translation.json',
}
});
export default i18n;
3. Create Translation Files
Organize your translations in JSON files within the public/locales directory. For example:
public/locales/en/translation.json:{"welcome": "Hello World"}public/locales/fr/translation.json:{"welcome": "Bonjour le monde"}
4. Use in Components:
Use the useTranslation hook to access the translation function (t) and the i18n instance.
/**
* useTranslation() in React
*/
import { useTranslation } from "react-i18next";
export default function App() {
const { t, i18n } = useTranslation();
const changeLanguage = (lng) => {
i18n.changeLanguage(lng);
};
return (
<>
<h2>{t("Welcome")}</h2>
<button onClick={() => changeLanguage("en")}>English</button>
<button onClick={() => changeLanguage("fr")}>French</button>
</>
);
}
⚝ Try this example on CodeSandbox
Reference:
# 22. REACT MISCELLANEOUS
Q. What is React DEV Tools?
React Developer Tools is a browser extension (available for Chrome, Firefox, and Edge) and a standalone app that lets you inspect, debug, and profile React component trees directly in the browser.
Installation:
# Chrome Extension
https://chrome.google.com/webstore/detail/react-developer-tools
# Firefox Extension
https://addons.mozilla.org/en-US/firefox/addon/react-devtools/
# Standalone (for React Native / non-browser environments)
npm install -g react-devtools
react-devtools
Two Main Panels:
1. Components Tab:
Displays the full React component tree for the current page. You can:
- Select any component to inspect its current
props,state,context, andhooks. - Edit props and state values live in the browser to test different scenarios without changing code.
- Highlight components in the page by hovering over them in the tree.
- Click the
<>icon to jump directly to the component's source code. - Filter the tree to hide DOM elements or specific component types.
2. Profiler Tab:
Records a performance profile of your React app to identify re-render bottlenecks. You can:
- Record interactions and see which components rendered, how long each render took, and why it re-rendered.
- View a Flamegraph (time breakdown per component) or a Ranked Chart (components sorted by render time).
- Identify components that are re-rendering unnecessarily and should be wrapped in
React.memo,useMemo, oruseCallback.
Key Features:
| Feature | Description |
|---|---|
| Component tree | Visualize the full hierarchy of React components |
| Props/State inspector | View and edit props, state, context, and hooks in real time |
| Component search | Quickly find components by name |
| Highlight updates | Flash components that re-render (helps spot unnecessary renders) |
| Profiler | Record and analyze render performance |
| Source mapping | Jump to component source code from DevTools |
| Strict Mode badge | See which components are inside <StrictMode> |
Highlight Re-renders:
In the Profiler settings, enable “Highlight updates when components render” to visually flash every component that re-renders. This is the fastest way to spot components that are re-rendering more than necessary.
Reference:
Q. What is render hijacking in React?
In React, render hijacking is a technique where one component controls or modifies the render output of another component. It is most commonly achieved using Higher-Order Components (HOCs), specifically through a pattern called Inheritance Inversion.
In Render Highjacking we can:
- Read, add, edit, remove props in any of the React Elements outputted by render
- Read, and modify the React Elements tree outputted by render
- Conditionally display the elements tree
- Wrapping the element's tree for styling purposes.
Common Use Cases
Render hijacking is typically used for advanced cross-cutting concerns:
- Access Control: Checking permissions and rendering a “Denied” message instead of the requested component.
- Loading States: Wrapping components to display a loader while waiting for data.
- Performance Optimization: Intercepting the render to prevent unnecessary updates or to cache results.
- Error Handling: Implementing Error Boundaries to catch crashes in child components and display a safe UI instead.
Example: Inheritance Inversion HOC
function withHijack(WrappedComponent) {
return class extends WrappedComponent {
render() {
// Hijack the render output
const elementsTree = super.render();
// Modify the output (e.g., adding a style or changing props)
if (elementsTree && elementsTree.type === 'div') {
return React.cloneElement(elementsTree, {
style: { color: 'blue' }
});
}
return elementsTree;
}
};
}
Q. What is windowing technique in react?
Windowing (also known as list virtualization) is a performance optimization technique that renders only the items currently visible in the user's viewport, plus a small buffer, rather than rendering an entire large dataset at once.
How It Works
Instead of mounting thousands of DOM nodes simultaneously—which can cause laggy scrolling and high memory usage—windowing calculates which elements should be visible based on the scroll position.
- Visible “Window”: Only the 5–10 items the user can see are rendered in the DOM.
- Dynamic Swapping: As the user scrolls, items exiting the viewport are unmounted, and new items entering the “window” are mounted.
- Absolute Positioning: A large “inner container” is used to simulate the total height of the full list, ensuring the scrollbar behaves accurately even though most items are not actually there.
Popular Libraries:
-
react-window: A modern, lightweight (approx. 2KB) library focused on speed and simplicity.
-
react-virtualized: A more feature-rich but larger library (approx. 33KB) for complex use cases like masonry layouts.
Example: react-window
import React from 'react';
import { FixedSizeList as List } from 'react-window';
// 1. Define the Row component
// It receives 'index' (to fetch data) and 'style' (crucial for positioning)
const Row = ({ index, style }) => (
<div style=>
Item {index}
</div>
);
const App = () => {
return (
// 2. Wrap the Row in a List component
<List
height={400} // Visible height of the "window"
width={300} // Width of the list
itemCount={10000} // Total number of items in the dataset
itemSize={50} // Height of each individual row in pixels
>
{Row}
</List>
);
};
export default App;
⚝ Try this example on CodeSandbox
Q. How to use the React Profiler to measure and improve performance?
The React Profiler is a built-in tool (available in React DevTools browser extension and programmatically via the <Profiler> component) that measures how often components render and what their render cost is.
1. React DevTools Profiler (Browser Extension)
Open Chrome/Firefox DevTools → React tab → Profiler tab:
- Click Record, interact with your app, then click Stop.
- View a flame chart showing which components rendered, how long they took, and why they re-rendered.
- Look for components that render unnecessarily (appear in every frame despite no prop changes).
2. Programmatic Profiling with <Profiler> component
import { Profiler } from 'react';
function onRenderCallback(
id, // Component tree identifier
phase, // 'mount' | 'update' | 'nested-update'
actualDuration, // Time spent rendering (ms)
baseDuration, // Estimated time without memoization (ms)
startTime,
commitTime
) {
console.log(`[Profiler] ${id} (${phase}): ${actualDuration.toFixed(2)}ms`);
// Send to analytics in production for real-user performance data
}
function App() {
return (
<Profiler id="Navigation" onRender={onRenderCallback}>
<Navigation />
</Profiler>
);
}
3. Identifying and fixing performance issues
// Step 1: Find the bottleneck — look for high actualDuration in Profiler
// Step 2: Check WHY a component re-rendered (hover flame chart bars in DevTools)
// Common fixes:
// Fix A: Memoize expensive child components
const ExpensiveList = React.memo(({ items }) => (
<ul>{items.map(item => <li key={item.id}>{item.name}</li>)}</ul>
));
// Fix B: Stabilize callback props with useCallback
const handleDelete = useCallback((id) => {
setItems(prev => prev.filter(item => item.id !== id));
}, []); // Stable reference → React.memo child doesn\'t re-render
// Fix C: Memoize derived data with useMemo
const sortedItems = useMemo(
() => [...items].sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
// Fix D: Split state to minimize re-render scope
// ❌ One big state object — any field update re-renders everything
const [formState, setFormState] = useState({ name: '', email: '', bio: '' });
// ✅ Separate state per field — each update only affects its own subtree
const [name, setName] = useState('');
const [email, setEmail] = useState('');
4. React DevTools Profiler interaction types explained
| Why it rendered | Meaning | Fix |
|---|---|---|
| “Props changed” | A prop reference changed | Check parent re-renders; use useMemo/useCallback |
| “State changed” | Local state update | Expected; localize state if too broad |
| “Context changed” | A context value changed | Split contexts; use selectors |
| “Parent re-rendered” | Parent re-rendered and child is not memoized | Wrap with React.memo |
Q. How to implement feature flags in React?
Feature flags (also called feature toggles) allow you to enable or disable features at runtime without deploying new code. They are used for A/B testing, gradual rollouts, canary releases, and kill switches.
1. Simple environment-variable approach
// .env
VITE_FEATURE_NEW_CHECKOUT=true
VITE_FEATURE_AI_SEARCH=false
// Component
function CheckoutButton() {
const isNewCheckout = import.meta.env.VITE_FEATURE_NEW_CHECKOUT === 'true';
return isNewCheckout
? <NewCheckoutButton />
: <LegacyCheckoutButton />;
}
2. Context-based feature flag provider
// FeatureFlagContext.jsx
const FeatureFlagContext = createContext({});
export function FeatureFlagProvider({ children }) {
const [flags, setFlags] = useState({
newDashboard: false,
aiSearch: false,
darkMode: true,
});
// Load flags from server/LaunchDarkly/etc.
useEffect(() => {
fetch('/api/feature-flags')
.then(res => res.json())
.then(setFlags);
}, []);
return (
<FeatureFlagContext.Provider value={flags}>
{children}
</FeatureFlagContext.Provider>
);
}
// Custom hook
export function useFeatureFlag(flagName) {
const flags = useContext(FeatureFlagContext);
return flags[flagName] ?? false;
}
// Usage in any component
function SearchBar() {
const hasAiSearch = useFeatureFlag('aiSearch');
return hasAiSearch
? <AiSearchBar />
: <BasicSearchBar />;
}
3. HOC pattern for feature-gated components
function withFeatureFlag(flagName, FallbackComponent = null) {
return function FeatureGatedComponent({ WrappedComponent, ...props }) {
const enabled = useFeatureFlag(flagName);
if (!enabled) return FallbackComponent ? <FallbackComponent {...props} /> : null;
return <WrappedComponent {...props} />;
};
}
4. Using LaunchDarkly (popular enterprise tool)
import { LDProvider, useFlags } from 'launchdarkly-react-client-sdk';
function App() {
return (
<LDProvider clientSideID="your-client-id" context=>
<Dashboard />
</LDProvider>
);
}
function Dashboard() {
const { newDashboard } = useFlags();
return newDashboard ? <NewDashboard /> : <OldDashboard />;
}
Feature flag strategies:
| Strategy | Use case |
|---|---|
| Boolean toggle | Simple on/off |
| Percentage rollout | Gradually expose to % of users |
| User targeting | Enable for specific user IDs/segments |
| A/B testing | Show variant A to 50%, B to 50% |
| Kill switch | Instantly disable a broken feature |
Q. Why does React Strict Mode invoke effects and lifecycle methods twice in development?
React StrictMode intentionally invokes certain functions twice in development to help you detect side effects that aren't properly cleaned up. This double-invocation does not happen in production.
What gets called twice:
- Function component bodies (render phase)
useState,useMemo,useReducerinitializer functionsuseEffectsetup and cleanup functions (mount → unmount → mount)- Class component
constructor,render,getDerivedStateFromProps,shouldComponentUpdate
Why this matters:
// ❌ Bug exposed by StrictMode — this fires twice on mount
useEffect(() => {
const subscription = eventBus.subscribe('event', handler);
// Missing cleanup! Second invocation creates a duplicate subscription.
}, []);
// ✅ Correct — cleanup properly cancels the subscription
useEffect(() => {
const subscription = eventBus.subscribe('event', handler);
return () => subscription.unsubscribe(); // Cleanup runs between the two mounts
}, []);
The double-mount lifecycle in StrictMode:
1. Component mounts → setup() runs (first mount)
2. Component unmounts → cleanup() runs (StrictMode unmount)
3. Component remounts → setup() runs again (StrictMode remount)
This simulates what happens in React's future Offscreen API (where components can be hidden and shown again while preserving state), ensuring your code handles mount/unmount cycles gracefully.
Common patterns that break under StrictMode:
// ❌ Broken — counter is incremented twice on first render
useEffect(() => {
analytics.trackPageView(); // Fires twice — duplicate analytics event!
}, []);
// ✅ Fixed with cleanup guard
useEffect(() => {
let active = true;
if (active) analytics.trackPageView(); // Still fires twice but idempotent pattern
return () => { active = false; };
}, []);
// ❌ Broken — WebSocket connection created twice with no cleanup
useEffect(() => {
const ws = new WebSocket(url);
ws.onmessage = handleMessage;
}, [url]);
// ✅ Fixed
useEffect(() => {
const ws = new WebSocket(url);
ws.onmessage = handleMessage;
return () => ws.close(); // Close on unmount/re-mount
}, [url]);
Key takeaway: If your code breaks in StrictMode, it means you have a real bug — a side effect that isn't idempotent or not properly cleaned up. StrictMode is a linter for effects, not a problem itself.
Q. How to optimize React Performance?
Optimizing React performance generally falls into three categories: reducing the size of your code, minimizing unnecessary rendering, and managing heavy data efficiently.
1. Reduce Initial Load (Bundle Size)
Large JavaScript bundles are a major cause of slow “Time to Interactive” (TTI).
-
Code Splitting: Use
React.lazy()andSuspenseto split your app into smaller chunks. Instead of loading every page at once, load only what the user needs for their current route. -
Tree Shaking: Ensure your build tool (like Vite or Webpack) removes unused code by using named imports (e.g.,
import { map } from 'lodash-es') instead of importing entire libraries. -
Production Build: Always deploy the minified production version (
npm run build). Development builds include extra warnings and checks that make the app significantly slower.
2. Prevent Unnecessary Re-renders
React re-renders a component whenever its parent renders or its state/props change. You can skip this work for components that haven't actually changed:
-
React.memo: Wrap pure functional components in
memo()to skip re-rendering if their props remain the same. -
useCallback & useMemo: Use these hooks to maintain “referential equality.” If you pass a new function or object as a prop on every render, it will break React.memo in child components. Caching them prevents this.
-
Localize State: Move state as low as possible in the component tree. If only one input field needs a piece of state, don't keep it in the root App component, as updating it will re-render everything.
3. Handle Large Data & Interactions
-
Windowing (Virtualization): For lists with hundreds or thousands of items, use libraries like react-window or react-virtualized. These render only the items currently visible in the viewport, drastically reducing DOM nodes.
-
Transition Hook (useTransition): Use this to mark non-urgent state updates (like filtering a long list). It allows React to keep the UI responsive for urgent tasks (like typing in an input) while the heavy rendering happens in the background.
-
Throttling & Debouncing: For events that fire rapidly (scrolling, resizing, typing), use lodash to limit how often your functions execute.
Q. What do you understand with the term polling in React?
Polling in React is a mechanism where the application repeatedly calls an API at regular intervals to check for updated data and refresh the UI accordingly.
How Polling works in React
React itself doesn't provide polling out of the box, so its implemented using:
- setInterval or setTimeout
- useEffect Hook
- API calls (Fetch/Axios)
There are two primary ways polling is implemented:
1. Short Polling
The client sends an HTTP request at a fixed interval (e.g., every 5 seconds) regardless of whether the data has actually changed.
- How it works: Uses setInterval or setTimeout within a useEffect hook to trigger a fetch function.
- Best for: Dashboards, notification counters, or status indicators where slight delays are acceptable.
- Drawback: Can be inefficient and resource-heavy for the server if many requests return no new data.
2. Long Polling
The client sends a request, and the server holds the connection open until new data is available or a timeout occurs.
- How it works: Once the client receives a response, it immediately initiates a new request to wait again.
- Best for: Scenarios needing closer to “real-time” responsiveness without the complexity of WebSockets.
Example: Recommended Approach (TanStack Query)
Using a library like TanStack Query (formerly React Query) is better because it handles background refetching, caching, and loading states automatically.
import { useQuery } from '@tanstack/react-query';
function UserActivity() {
const { data, isLoading } = useQuery({
queryKey: ['activeUsers'],
queryFn: () => fetch('/api/active-users').then(res => res.json()),
// Polls every 3 seconds
refetchInterval: 3000,
// Continues polling even when the tab is in the background
refetchIntervalInBackground: true
});
if (isLoading) return <div>Loading...</div>;
return <div>Active Users: {data.count}</div>;
}
Q. What are the drawbacks of MVW pattern?
MVW stands for Model-View-Whatever
MVC- Model-View-ControllerMVP- Model-View-PresenterMVVM- Model-View-ViewModelMVW / MV* / MVx- Model-View-WhateverHMVC- Hierarchical Model-View-ControllerMMV- Multiuse Model ViewMVA- Model-View-Adapter
In React.js, the MVW (Model-View-Whatever) pattern—which typically encompasses variations like MVC (Model-View-Controller) or MVVM (Model-View-ViewModel)—is often considered a “mismatch” for React's core philosophy.
Drawbacks as applications scale:
-
Uncontrolled state changes — As the number of models and controllers grows, they start communicating with each other through service layers, each modifying the other's state. It becomes increasingly difficult to track who changed what and when.
-
Nondeterministic UI state — Asynchronous network calls make the timing of model updates unpredictable. If a user interacts with the UI while an async callback is still in-flight, the resulting state can be inconsistent and hard to reason about.
-
Complex mutation tracking — Determining when state has actually changed adds another layer of complexity. You need additional tooling or conventions just to detect and respond to mutations reliably.
-
Poor fit for real-time/collaborative apps — Applications like Google Docs, where many data changes happen simultaneously from multiple sources, expose the fundamental weaknesses of MVW's bidirectional data flow.
-
No built-in undo/time-travel — Implementing undo/redo requires significant extra boilerplate because there is no single source of truth or immutable state history.
React's unidirectional data flow (actions → reducer → state → UI) and Redux's single immutable state tree were specifically designed to solve these MVW problems — making state changes predictable, traceable, and replayable.
Q. How do you update render elements?
React Elements are immutable — once created, you cannot update their children or attributes directly. To update a rendered element, you must re-render with a new element.
Using useState + useEffect (Recommended approach):
In practice, managing periodic updates via setInterval inside a component using hooks is the idiomatic React way. State changes trigger re-renders automatically.
Example:
import { useState, useEffect } from "react";
function Clock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(id); // cleanup on unmount
}, []);
return <h2>Current Time: {time.toLocaleTimeString()}</h2>;
}
export default Clock;
React's diffing algorithm ensures only the changed part of the DOM (the time string) is updated on each re-render.
⚝ Try this example on CodeSandbox
Q. What is the difference between rendering and mounting in ReactJS?
Mounting happens once — when React inserts a component into the DOM for the first time and builds the initial DOM nodes from the component's output.
Rendering happens every time React calls the component function (or render() method) to get instructions for what the DOM should look like. This occurs on mount and on every subsequent re-render.
Re-rendering is when an already-mounted component renders again due to a state or prop change — React calls the component again, diffs the new output against the previous one, and updates only what changed.
Example:
import { useState, useEffect } from "react";
/**
* Message Component
* Mounts when showMessage is true, unmounts when false.
*/
function Message({ name }) {
// Runs once after this component is mounted into the DOM
useEffect(() => {
console.log("Message mounted");
return () => console.log("Message unmounted"); // cleanup on unmount
}, []);
// Runs after every re-render when `name` prop changes
useEffect(() => {
console.log("Message re-rendered, name =", name);
}, [name]);
return <h2>{name}</h2>;
}
/**
* App Component
* Renders on every state change, but Message only mounts/unmounts
* when showMessage toggles between true and false.
*/
export default function App() {
const [showMessage, setShowMessage] = useState(false);
const [name, setName] = useState("Hello React!");
return (
<div>
<button onClick={() => setShowMessage(true)}>Show Message</button>
<button onClick={() => setShowMessage(false)}>Hide Message</button>
<button onClick={() => setName("Hello World!")}>Change Name</button>
{showMessage && <Message name={name} />}
</div>
);
}
⚝ Try this example on CodeSandbox
Q. What is Flow in react?
Type Checking:
Type checking means ensuring that the type of a property (variable, object, function, string) in a programming language is being used as it should be. It is the process of verifying and enforcing the constraints of types, and it can occur either at compile time or at runtime. It helps to detect and report errors.
Type checking can be divided into two: static type checking and dynamic type checking.
1. Static Type Checking:
Static type checking is used in static-typed languages where the type of the variable is known at the compile time. This means that the type of the variable must be declared beforehand. Static typing usually results in compiled code that executes more quickly because the compiler already knows the exact data types that are in use.
2. Dynamic type checking:
Dynamic type checking is used in dynamic-typed languages where the type is usually known at runtime. This means that the type of the variable doesn't need to be explicitly defined.
Flow:
Flow is a static type checker for JavaScript apps that aims to find and eliminate problems as you code. Designed by the Facebook team for JavaScript developers, it's a static type checker that catches common errors in your application before they run.
Integrating Flow:
# Create React App with Flowchecker
npx create-react-app flowchecker
# Add Dependency
npm install --save-dev flow-bin
The next thing to do is add Flow to the “scripts” section of your package.json so that Flow can be used in the terminal. In the package.json file, add the code snippet below.
"scripts": {
"flow": "flow",
}
Finally, for the Flow setup, run any of the commands below:
npm run flow init
This will help to create a Flow configuration file that should be committed. The Flow config file helps to determine the files that Flow should work with and what should be ignored.
Flow vs PropTypes vs TypeScript
| Flow | PropTypes | TypeScript | |
|---|---|---|---|
| Check time | Compile time | Runtime | Compile time |
| Scope | Entire codebase | Props only | Entire codebase |
| Recommended for | Medium/large apps | Small apps | Large apps |
Note:
React recommends using Flow or TypeScript over PropTypes for larger codebases, as they catch a broader class of bugs at compile time rather than at runtime.
Q. What is the difference between Flow and PropTypes?
Flow is a static analysis tool which uses a superset of the language, allows to add type annotations to all of your code and catch an entire class of bugs at compile time.
PropTypes is a basic type checker which has been patched onto React. It can not check anything other than the types of the props being passed to a given component.
Q. What is React Fiber?
React Fiber is the reimplemented reconciliation algorithm introduced in React 16. It replaced the old Stack Reconciler, which processed component trees synchronously in a single pass — blocking the main thread from handling animations, layouts, and gestures.
Core Goals of Fiber
- Split interruptible work into chunks
- Prioritize work (urgent vs. non-urgent tasks)
- Pause, reuse, or abort work in progress
- Support returning multiple elements from
render()
What is a Fiber?
A fiber is a plain JavaScript object representing a unit of work for a component. At any time, a component has at most two fibers:
current— what's currently renderedwork-in-progress— what's being calculated
React Fiber performs reconciliation in two phases: Render and Commit
1. Lifecycle methods called during render phase:(Asynchronous)
UNSAFE_componentWill*getDerivedStateFromProps()shouldComponentUpdate()render()
2. Lifecycle methods called during commit phase:(Synchronous)
getSnapshotBeforeUpdate()componentDidMount()componentDidUpdate()componentWillUnmount()
The render phase is asynchronous — this is why three lifecycle methods were marked UNSAFE_: their execution order is no longer guaranteed.
React Fiber uses requestIdleCallback() to schedule the low priority work and requestAnimationFrame() to schedule high priority work.
Problems with Current Implementation:
- Long-running tasks cause frame drops.
- Different tasks have different priorities.
How React Fiber works:
- It makes apps more fluid and responsible.
- In the future, it could parallelize work a.k.a. Time Slicing.
- It would improve startup time while rendering components using React Suspense.
Fiber is currently available for use but it runs in compatibility mode with the current implementation.
Q. What are the key new features introduced in React 19?
React 19 (released in 2024) introduced several major features focused on simplifying async data handling, form management, and reducing the need for manual memoization.
1. Actions — async state management made easy
React 19 introduced Actions, which are async functions used with form action props or useTransition. They automatically handle pending states, errors, and optimistic updates.
// React 19 — <form> with async action (no onSubmit needed)
function UpdateName() {
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const submitAction = async (formData) => {
startTransition(async () => {
const { error } = await updateUsername(formData.get('username'));
if (error) setError(error);
});
};
return (
<form action={submitAction}>
<input name="username" />
<button disabled={isPending}>{isPending ? 'Saving...' : 'Save'}</button>
{error && <p>{error}</p>}
</form>
);
}
2. useActionState (formerly useFormState)
Manages the state and pending status of async form actions:
import { useActionState } from 'react';
async function submitForm(prevState, formData) {
const result = await saveData(formData.get('name'));
return result.error ? { error: result.error } : { success: true };
}
function Form() {
const [state, action, isPending] = useActionState(submitForm, null);
return (
<form action={action}>
<input name="name" />
<button disabled={isPending}>Submit</button>
{state?.error && <p className="error">{state.error}</p>}
{state?.success && <p>Saved!</p>}
</form>
);
}
3. useOptimistic — optimistic UI updates
Renders an optimistic value immediately while an async operation completes:
import { useOptimistic } from 'react';
function TodoList({ todos, addTodo }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(currentTodos, newTodo) => [...currentTodos, { ...newTodo, pending: true }]
);
async function handleAdd(formData) {
const newTodo = { id: Date.now(), text: formData.get('text') };
addOptimisticTodo(newTodo); // Show immediately (optimistic)
await saveTodo(newTodo); // Persist to server
}
return (
<form action={handleAdd}>
<input name="text" />
<button>Add</button>
<ul>
{optimisticTodos.map(todo => (
<li key={todo.id} style=>
{todo.text}
</li>
))}
</ul>
</form>
);
}
4. use() — read Promises and Context in render
The new use() hook can read a Promise (suspending if not resolved) or a Context — and unlike other hooks, it can be called conditionally:
import { use, Suspense } from 'react';
// Reading a Promise (triggers Suspense while pending)
function UserProfile({ userPromise }) {
const user = use(userPromise); // Suspends until promise resolves
return <h1>{user.name}</h1>;
}
// Can be called conditionally (unlike other hooks)
function Component({ shouldRead }) {
if (shouldRead) {
const value = use(MyContext); // ✅ Conditional use() is allowed
}
}
5. React Server Actions
Server Actions are async functions that run on the server and can be called directly from Client Components:
// actions.js (Server Action)
'use server';
export async function updateProfile(formData) {
const name = formData.get('name');
await db.users.update({ name });
}
// Component (Client or Server)
import { updateProfile } from './actions';
function ProfileForm() {
return <form action={updateProfile}><input name="name" /><button>Save</button></form>;
}
6. Improved ref handling
Function components can now accept ref as a regular prop — no forwardRef() needed:
// React 19 — ref as a normal prop
function Input({ placeholder, ref }) {
return <input ref={ref} placeholder={placeholder} />;
}
// Usage
const inputRef = useRef(null);
<Input ref={inputRef} placeholder="Type here" />
React 19 feature summary:
| Feature | Description |
|---|---|
useActionState |
Manages async form action state |
useFormStatus |
Reads pending/data state of parent form |
useOptimistic |
Optimistic UI during async operations |
use() |
Read Promises and Context; callable conditionally |
| Server Actions | 'use server' functions callable from client |
ref as prop |
No more forwardRef() needed |
<Context> as provider |
Use <MyContext value={...}> instead of <MyContext.Provider> |
Q. Does the static object work with ES6 classes in React?
Yes, Although statics only works with the legacy React.createClass(), you can still write static methods in ES6 notation. If you are using ES7, then you can also write static properties.
Example:
import React from 'react';
import PropTypes from 'prop-types';
class UserCard extends React.Component {
// ES7 static property: type validation
static propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
role: PropTypes.oneOf(['admin', 'user', 'guest'])
};
// ES7 static property: default values
static defaultProps = {
age: 0,
role: 'user'
};
// ES6 static method: utility / factory helper
static formatRole(role) {
return role.charAt(0).toUpperCase() + role.slice(1);
}
render() {
const { name, age, role } = this.props;
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Role: {UserCard.formatRole(role)}</p>
</div>
);
}
}
// Static methods can be called directly on the class (no instance needed)
console.log(UserCard.formatRole('admin')); // "Admin"
export default UserCard;
Q. How do you access imperative API of web components?
Web Components often expose an imperative API (methods you call directly on the DOM element, like .play(), .focus(), .open()). Since React's declarative model does not directly call these, you need a ref to access the underlying DOM node.
Example: Using React in your Web Components
// 1. Define the Web Component (vanilla JS)
import { createRoot } from 'react-dom/client';
class XSearch extends HTMLElement {
connectedCallback() {
const mountPoint = document.createElement('span');
this.attachShadow({ mode: 'open' }).appendChild(mountPoint);
const name = this.getAttribute('name');
const url = 'https://www.google.com/search?q=' + encodeURIComponent(name);
createRoot(mountPoint).render(<a href={url}>{name}</a>);
}
}
customElements.define('x-search', XSearch);
// 2. Use the Web Component from a React Functional Component
import { useRef, useEffect } from 'react';
function SearchComponent({ name }) {
const searchRef = useRef(null);
useEffect(() => {
if (searchRef.current) {
// Access the web component\'s imperative API via the ref
// e.g., searchRef.current.someMethod();
}
}, [name]);
return (
<div>
Results: <x-search ref={searchRef} name={name}></x-search>!
</div>
);
}
export default SearchComponent;
Q. What is the purpose of eslint plugin for hooks?
The ESLint plugin ( eslint-plugin-react-hooks ) enforces rules of Hooks to avoid bugs. It assumes that any function starting with “use” and a capital letter right after it is a Hook. In particular, the rule enforces that,
- Calls to Hooks are either inside a PascalCase function (assumed to be a component) or another useSomething function (assumed to be a custom Hook).
- Hooks are called in the same order on every render.
// ESLint configuration
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
"react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
}
}
Note:
This plugin is included by default in Create React App.
Q. What is service worker in React.js?
A service worker is a background worker that acts as a programmable proxy, allowing us to control what happens on a request-by-request basis. We can use it to make (parts of, or even entire) React apps work offline.
Service workers depend on two APIs to work effectively: Fetch (a standard way to retrieve content from the network) and Cache (content storage for application data. This cache is independent from the browser cache or network status).
Service Worker Lifecycle:
Each service worker goes through three steps in its lifecycle: registration, installation and activation.
Registration:
To install a service worker, you need to register it in script. Registration informs the browser where your service worker is located and lets it know it can start installing in the background.
Example: Basic registration in your index.html could look like this
// Check for browser support of service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(function(registration) {
// Successful registration
console.log('Registration successful, scope is:', registration.scope);
}).catch(function(err) {
// Failed registration, service worker won\'t be installed
console.log('Service worker registration failed, error:', error);
});
}
Installation and activation:
Service workers are event driven. The installation and activation processes fire off corresponding install and activate events to which the service workers can respond.
With the service worker registered, the first time a user hits your PWA, the install event will be triggered and this is where you'll want to cache the static assets for the page.
Q. What is the purpose of registerServiceWorker in React?
React creates a service worker for you without any configuration by default. The service worker is a web API that helps you cache your assets and other files so that when the user is offline or on slow network, user can still see results on the screen.
Example: Enable service worker in react
/**
* Register Service Worker
*/
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
const root = createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// Register the service worker for offline support and faster loads.
// Pass optional callbacks to handle lifecycle events:
serviceWorkerRegistration.register({
onSuccess: (registration) => {
console.log('Service Worker registered successfully:', registration.scope);
},
onUpdate: (registration) => {
// Notify the user that a new version is available
console.log('New content available; please refresh.', registration);
},
});
// To opt out of service workers entirely, call unregister() instead:
serviceWorkerRegistration.unregister();
Q. How to implement Push Notifications in a React PWA?
Push Notifications allow a server to send messages to a user's browser even when the web app is not open. They require a Service Worker (to receive pushes in the background) and the Push API + Notification API.
Step 1: Request notification permission
// In your React component
async function requestNotificationPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('Notification permission granted');
}
}
Step 2: Subscribe to Push notifications (client)
async function subscribeToPush(registration) {
const VAPID_PUBLIC_KEY = import.meta.env.VITE_VAPID_PUBLIC_KEY;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true, // Required: notifications always shown to user
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
});
// Send the subscription to your server to store
await fetch('/api/push/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(subscription),
});
}
// Helper: convert VAPID key
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const rawData = atob(base64);
return Uint8Array.from([...rawData].map(c => c.charCodeAt(0)));
}
Step 3: Handle push events in the Service Worker
// public/sw.js
self.addEventListener('push', (event) => {
const data = event.data?.json() ?? { title: 'New Notification', body: '' };
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: '/pwa-192x192.png',
badge: '/badge-72x72.png',
data: { url: data.url },
})
);
});
// Handle notification click
self.addEventListener('notificationclick', (event) => {
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data.url || '/'));
});
Step 4: React component wiring
import { useEffect } from 'react';
export function usePushNotifications() {
useEffect(() => {
if (!('serviceWorker' in navigator) || !('PushManager' in window)) return;
navigator.serviceWorker.ready.then(async (registration) => {
const existing = await registration.pushManager.getSubscription();
if (!existing) {
await requestNotificationPermission();
await subscribeToPush(registration);
}
});
}, []);
}
Key considerations:
- Push notifications require HTTPS (except
localhost). - Use VAPID keys (
web-pushpackage on Node.js) for secure server-to-browser messaging. - Always call
event.waitUntil()in service worker event handlers to prevent early termination.
Q. What is Background Sync in a PWA and how does it work with React?
Background Sync is a Service Worker API that allows a web application to defer tasks (like form submissions or data uploads) until the user has a stable internet connection — even if the app is closed.
How it works:
User performs action (e.g., submits form)
↓
App is offline → Store request in IndexedDB
↓
Register a sync event with the Service Worker
↓
Device reconnects to internet
↓
Service Worker fires 'sync' event
↓
Service Worker replays the stored request
↓
User's data is successfully sent to the server
Step 1: Register a sync tag (React component)
async function submitFormWithBackgroundSync(formData) {
if ('serviceWorker' in navigator && 'SyncManager' in window) {
// Store data locally first
await saveToIndexedDB('pending-forms', formData);
// Register a sync event
const registration = await navigator.serviceWorker.ready;
await registration.sync.register('submit-form');
console.log('Form queued for background sync');
} else {
// Fallback: submit directly
await fetch('/api/submit', { method: 'POST', body: JSON.stringify(formData) });
}
}
Step 2: Handle sync event in the Service Worker
// public/sw.js
self.addEventListener('sync', (event) => {
if (event.tag === 'submit-form') {
event.waitUntil(replayPendingForms());
}
});
async function replayPendingForms() {
const pendingForms = await getFromIndexedDB('pending-forms');
for (const formData of pendingForms) {
try {
await fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});
await removeFromIndexedDB('pending-forms', formData.id);
} catch (err) {
// Will retry on next sync event
throw err;
}
}
}
Step 3: React hook for offline-aware submission
function useOfflineForm() {
const [status, setStatus] = useState('idle');
const submitForm = async (data) => {
setStatus('submitting');
if (navigator.onLine) {
await fetch('/api/submit', { method: 'POST', body: JSON.stringify(data) });
setStatus('success');
} else {
await submitFormWithBackgroundSync(data);
setStatus('queued');
}
};
return { submitForm, status };
}
Background Sync vs Periodic Background Sync:
| Feature | Background Sync | Periodic Background Sync |
|---|---|---|
| Trigger | Network reconnect | Time interval (e.g., every hour) |
| User visible | No | No |
| Permission required | No | Yes (via periodicSync permission) |
| Use case | Retry failed requests | Refresh cached content |
| Browser support | Good | Limited |
Q. How Virtual-DOM is more efficient than Dirty checking?
React Virtual DOM:
In React, Each time the DOM updates or data of page changes, a new Virtual DOM representation of the user interface is made. It is just a lightweight copy or DOM.
Virtual DOM in React has almost same properties like a real DOM, but it can not directly change the content on the page. Working with Virtual DOM is faster as it does not update anything on the screen at the same time. In a simple way, Working with Virtual DOM is like working with a copy of real DOM nothing more than that.
Updating virtual DOM in ReactJS is faster because ReactJS uses
- It is efficient diff algorithm.
- It batched update operations
- It efficient update of sub tree only
- It uses observable instead of dirty checking to detect change
How Virtual DOM works in React:
When we render a JSX element, each virtual DOM updates. This approach updates everything very quickly. Once the Virtual DOM updates, React matches the virtual DOM with a virtual DOM copy that was taken just before the update. By Matching the new virtual DOM with pre-updated version, React calculates exactly which virtual DOM has changed. This entire process is called diffing.
When React knows which virtual DOM has changed, then React updated those objects. and only those object, in the real DOM. React only updates the necessary parts of the DOM. React's reputation for performance comes largely from this innovation.
In brief, here is what happens when we update the DOM in React:
- The entire virtual DOM gets updated.
- The virtual DOM gets compared to what it looked like before you updated it. React matches out which objects have changed.
- The changed objects and the changed objects only get updated on the real DOM.
- Changes on the real DOM cause the screen to change finally.
Q. What is the difference between DOM and virtual DOM?
1. DOM:
DOM stands for “Document Object Model”. The HTML DOM provides an interface (API) to traverse and modify the nodes. It contains methods like getElementById() or removeChild().
The DOM is represented as a tree data structure. Because of that, the changes and updates to the DOM are fast. But after the change, the updated element and it's children have to be re-rendered to update the application UI. The re-rendering or re-painting of the UI is what makes it slow.
2. Virtual DOM:
The virtual DOM is only a virtual representation of the DOM. Every time the state of our application changes, the virtual DOM gets updated instead of the real DOM.
The Virtual DOM is an abstraction of the HTML DOM. It is lightweight and detached from the browser-specific implementation details. Since the DOM itself was already an abstraction, the virtual DOM is, in fact, an abstraction of an abstraction.
Why Virtual DOM is faster:
When new elements are added to the UI, a virtual DOM, which is represented as a tree is created. Each element is a node on this tree. If the state of any of these elements changes, a new virtual DOM tree is created. This tree is then compared or “diffed” with the previous virtual DOM tree.
Once this is done, the virtual DOM calculates the best possible method to make these changes to the real DOM. This ensures that there are minimal operations on the real DOM. Hence, reducing the performance cost of updating the real DOM.
Pros of Virtual DOM:
- Updates process is optimized and accelerated.
- JSX makes components/blocks code readable.
- React data binding establishes conditions for creation dynamic applications.
- Virtual DOM is ideal for mobile first applications.
- Prompt rendering. Using comprises methods to minimize number of DOM operations helps to optimize updating process and accelerate it.
Q. Explain DOM diffing?
Once the Virtual DOM is created, React compares this new representation with a snapshot of the previous version of the virtual DOM to see exactly which elements have changed.
Once the difference is known, React updates only those objects that differ on the actual DOM and the browser re-paints the screen. The next time state or props changes for a component in the application, a new virtual DOM tree of React elements will be created and the process will repeat.
The process of checking the difference between the new Virtual DOM tree and the old Virtual DOM tree is called diffing. Diffing is accomplished by a heuristic O(n) algorithm. During this process, React will deduce the minimum number of steps needed to update the real DOM, eliminating unnecessary costly changes. This process is also referred to as reconciliation.
React implements a heuristic O(n) algorithm based on two assumptions:
- Two elements of different types will produce different trees.
- The developer can hint at which child elements may be stable across different renders with a key prop.”
Q. How Diff Algorithm is implemented in React.js?
The main work of a diff algorithm is to find a heuristic to change anything from a state to another. When diffing two trees, React first compares the two root elements. The behavior is different depending on the types of the root elements.
1. Elements Of Different Types:
Whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch.
When tearing down a tree, old DOM nodes are destroyed. Component instances receive componentWillUnmount().
When building up a new tree, new DOM nodes are inserted into the DOM. Component instances receive UNSAFE_componentWillMount() and then componentDidMount(). Any state associated with the old tree is lost.
2. DOM Elements Of The Same Type:
When comparing two React DOM elements of the same type, React looks at the attributes of both, keeps the same underlying DOM node, and only updates the changed attributes.
Example: By comparing these two elements, React knows to only modify the className on the underlying DOM node.
<div className="before" title="React JS" />
<div className="after" title="React JS" />
3. Component Elements Of The Same Type:
When a component updates, the instance stays the same, so that state is maintained across renders. React updates the props of the underlying component instance to match the new element, and calls UNSAFE_componentWillReceiveProps(), UNSAFE_componentWillUpdate() and componentDidUpdate() on the underlying instance.
Recursing On Children:
By default, when recursing on the children of a DOM node, React just iterates over both lists of children at the same time and generates a mutation whenever there's a difference.
For example, when adding an element at the end of the children, converting between these two trees works well:
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
React will match the two <li>first</li> trees, match the two <li>second</li> trees, and then insert the <li>third</li> tree.
Keys:
When children have keys, React uses the key to match children in the original tree with children in the subsequent tree. For example, adding a key to our inefficient example above can make the tree conversion efficient:
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
Q. What is reconciliation in React?
Reconciliation is the process React uses to determine how to efficiently update the real browser DOM when a component's state or props change. Instead of rebuilding the entire UI from scratch, React compares a new representation of the interface with the previous one and applies only the necessary changes.
How the Process Works
- Virtual DOM Creation: When an update is triggered, React creates a new Virtual DOM (a lightweight, in-memory copy of the UI).
- Diffing: React uses a “diffing algorithm” to compare the new Virtual DOM tree with the old one.
- Patching: Once the differences are identified, React calculates the minimal set of operations needed to update the actual DOM and applies them in a single batch to ensure high performance.
Key Rules of the Diffing Algorithm
To keep the reconciliation process fast (complexity), React follows two primary heuristic assumptions:
- Elements of Different Types: If the root elements of a subtree have different types (e.g., changing a
<div>to a<span>), React will tear down the old tree, destroy its state, and build the new one from scratch. - Elements of the Same Type: If elements are of the same type, React keeps the underlying DOM node and only updates the changed attributes or properties.
- Keys for Lists: For lists of elements, React uses the key prop to track which items have moved, been added, or removed, preventing unnecessary re-renders of stable items.
Evolution: React Fiber
Since React 16, the core reconciliation engine has been reimplemented as React Fiber. This modern architecture allows React to break rendering work into small units and pause, resume, or prioritize them, making complex UIs much more responsive by not blocking the main thread
Q. What are portals in React?
Portals provide a quick and seamless way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
Normally, a functional or a class component renders a tree of React elements (usually generated from JSX). The React element defines how the DOM of the parent component should look.
Syntax:
import { createPortal } from 'react-dom';
function MyPortal({ children }) {
// Renders 'children' into the document body,
// away from this component physical DOM parent.
return createPortal(children, document.body);
}
Features:
- It transports its children component into a new React portal which is appended by default to
document.body. - It can also target user specified DOM element.
- It supports server-side rendering
- It supports returning arrays (no wrapper div's needed)
- It uses
<Portal />and<PortalWithState />so there is no compromise between flexibility and convenience.
Installation:
npm install react-portal --save
Example:
/**
* React Portal
*/
import PortalExample from "./PortalExample.js";
export default function App() {
return (
<div>
<h2>React Component Example</h2>
<PortalExample />
</div>
);
}
/**
* Portal Component
*/
import { createPortal } from 'react-dom';
export default function PortalExample() {
return createPortal(
<h2>React Portal Example</h2>,
document.getElementById("portal-root")
);
}
Now, open the Index.html file and add a <div id="portal-root"></div> element to access the child component outside the root node.
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App using Portal</title>
</head>
<body>
<noscript>It is required to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="portal-root"></div>
</body>
</html>
⚝ Try this example on CodeSandbox
Q. What is ReactDOMServer?
ReactDOMServer is an object that enables you to render React components to static HTML markup, typically used on a Node.js server for Server-Side Rendering (SSR).
// ES modules
import ReactDOMServer from 'react-dom/server'
// CommonJS
var ReactDOMServer = require('react-dom/server')
The Server-side rendering (SSR) is a popular technique for rendering a client-side single page application (SPA) on the server and then sending a fully rendered page to the client. This allows for dynamic components to be served as static HTML markup.
- It allows your site to have a faster first page load time, which is the key to a good user experience
- This approach can be useful for search engine optimization (SEO) when indexing does not handle JavaScript properly.
- It is great when people share a page of your site on social media, as they can easily gather the metadata needed to nicely share the link (images, title, description..)
Example:
Creating an Express Server:
npm install express
All the content inside the build folder is going to be served as-is, statically by Express.
/**
* server/server.js
*/
import path from 'path'
import fs from 'fs'
import express from 'express'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import App from '../src/App'
const PORT = 8080
const app = express()
const router = express.Router()
const serverRenderer = (req, res, next) => {
fs.readFile(path.resolve('./build/index.html'), 'utf8', (err, data) => {
if (err) {
console.error(err)
return res.status(500).send('An error occurred')
}
return res.send(
data.replace(
'<div id="root"></div>',
`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
)
)
})
}
router.use('^/$', serverRenderer)
router.use(
express.static(path.resolve(__dirname, '..', 'build'), { maxAge: '30d' })
)
// tell the app to use the above rules
app.use(router)
// app.use(express.static('./build'))
app.listen(PORT, () => {
console.log(`SSR running on port ${PORT}`)
})
Now, in the client application, in your src/index.js, instead of calling ReactDOM.render():
ReactDOM.render(<App />, document.getElementById('root'))
call ReactDOM.hydrate(), which is the same but has the additional ability to attach event listeners to existing markup once React loads:
ReactDOM.hydrate(<App />, document.getElementById('root'))
All the Node.js code needs to be transpiled by Babel, as server-side Node.js code does not know anything about JSX, nor ES Modules (which we use for the include statements).
Babel Package:
npm install @babel/register @babel/preset-env @babel/preset-react ignore-styles
Let's create an entry point in server/index.js:
require('ignore-styles')
require('@babel/register')({
ignore: [/(node_modules)/],
presets: ['@babel/preset-env', '@babel/preset-react']
})
require('./server')
Build the React application, so that the build/ folder is populated and run this:
# Build App
npm run build
# Run App on Express
node server/index.js
Q. How can automated tooling be used to improve the accessibility of a React application?
There are two main categories of automated tools that can be used to identify accessibility issues:
1. Static Analysis Tools (Compile-time)
Linting tools like ESLint with the eslint-plugin-jsx-a11y plugin analyze React projects at the component level — catching issues before code even runs.
Examples of issues it catches:
- Images missing
altattributes - Form inputs missing associated
<label>elements - Interactive elements with no keyboard support
- Invalid ARIA roles or attributes
2. Browser Tools (Runtime)
Browser accessibility tools such as aXe and Google Lighthouse perform automated accessibility at the app level. This can discover more real-world issues, because a browser is used to simulate the way that a user interacts with a website.
Q. How to do SEO on react?
React applications are typically Single Page Applications (SPAs) that render content on the client-side, which can pose challenges for SEO since search engine crawlers may not execute JavaScript. Here are the main approaches to optimize React apps for SEO:
1. Server-Side Rendering (SSR):
SSR renders React components on the server and sends fully rendered HTML to the client. This ensures search engines can crawl and index your content.
Use Next.js to render pages on the server before sending HTML to the client:
npm install next react react-dom
// pages/index.js
export default function Home({ data }) {
return (
<div>
<h1>Welcome to SEO-Optimized React</h1>
<p>{data.content}</p>
</div>
)
}
// This function runs on the server
export async function getServerSideProps() {
const data = await fetchDataFromAPI()
return {
props: { data }
}
}
2. Static Site Generation (SSG):
Pre-render pages at build time with getStaticProps() / getStaticPaths() — best for content that doesn't change frequently (blogs, docs).
// pages/blog/[slug].js
export default function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
)
}
export async function getStaticProps({ params }) {
const post = await getPostBySlug(params.slug)
return {
props: { post }
}
}
export async function getStaticPaths() {
const posts = await getAllPosts()
return {
paths: posts.map(post => ({
params: { slug: post.slug }
})),
fallback: false
}
}
3. Dynamic Meta Tags (React Helmet)
Use react-helmet-async to set page-specific <title>, <meta> descriptions, and Open Graph tags per route.
npm install react-helmet-async
import { Helmet } from 'react-helmet-async'
function ProductPage({ product }) {
return (
<>
<Helmet>
<title>{product.name} | My Store</title>
<meta name="description" content={product.description} />
<meta property="og:title" content={product.name} />
<meta property="og:description" content={product.description} />
<meta property="og:image" content={product.image} />
<meta property="og:url" content={`https://mystore.com/products/${product.id}`} />
<meta name="twitter:card" content="summary_large_image" />
<link rel="canonical" href={`https://mystore.com/products/${product.id}`} />
</Helmet>
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
</>
)
}
4. Pre-rendering
Use react-snap to generate static HTML snapshots at build time — a lighter alternative to full SSR.
npm install react-snap --save-dev
// package.json
{
"scripts": {
"postbuild": "react-snap"
},
"reactSnap": {
"inlineCss": true,
"minifyHtml": true
}
}
5. Dynamic Rendering:
Serve pre-rendered content to search engines while serving the SPA to users.
// server.js (Express example)
const express = require('express')
const puppeteer = require('puppeteer')
const app = express()
app.get('*', async (req, res) => {
const isBot = /bot|googlebot|crawler|spider|robot|crawling/i.test(
req.headers['user-agent']
)
if (isBot) {
// Serve pre-rendered content to bots
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto(`http://localhost:3000${req.url}`)
const html = await page.content()
await browser.close()
res.send(html)
} else {
// Serve SPA to regular users
res.sendFile('index.html')
}
})
Q. Explain SEO Best Practices for React?
1. Semantic HTML:
Use proper HTML5 elements (<article>, <header>, <section>, etc.) instead of generic <div> tags. This helps crawlers understand your content structure.
function Article({ title, content }) {
return (
<article>
<header>
<h1>{title}</h1>
</header>
<section>{content}</section>
</article>
)
}
2. Structured Data (JSON-LD):
Embed schema.org markup to help search engines understand entities like products, articles, and organizations.
function ProductStructuredData({ product }) {
const structuredData = {
"@context": "https://schema.org/",
"@type": "Product",
"name": product.name,
"image": product.image,
"description": product.description,
"brand": {
"@type": "Brand",
"name": product.brand
},
"offers": {
"@type": "Offer",
"price": product.price,
"priceCurrency": "USD"
}
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML=
/>
)
}
3. Image Optimization:
Always include:
- Descriptive
altattributes (for accessibility + SEO) - Explicit
widthandheight(reduces Cumulative Layout Shift) loading="lazy"for below-the-fold images
function OptimizedImage({ src, alt }) {
return (
<img
src={src}
alt={alt}
loading="lazy"
width="800"
height="600"
/>
)
}
4. URL Structure:
Use clean, descriptive paths instead of hash-based routing:
// Good SEO-friendly URLs
<Link to="/blog/react-seo-guide">React SEO Guide</Link>
// Avoid hash-based routing
// Bad: /#/blog/react-seo-guide
5. sitemap.xml and robots.txt:
Place these in /public:
- sitemap.xml — lists all crawlable URLs with priority and last-modified date
- robots.txt — tells crawlers what to index; includes a reference to your sitemap
<!-- public/sitemap.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<lastmod>2026-02-03</lastmod>
<priority>1.0</priority>
</url>
<url>
<loc>https://example.com/about</loc>
<lastmod>2026-02-03</lastmod>
<priority>0.8</priority>
</url>
</urlset>
# public/robots.txt
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
Q. Explain popular React SEO Tools?
Popular React SEO Tools:
- Next.js - React framework with built-in SSR/SSG
- Gatsby - Static site generator for React
- React Helmet Async - Manage document head
- React Snap - Pre-render SPA at build time
- React Static - Static site generator
Reference:
Q. What is Server-Side Rendering (SSR) and when should you use it?
Server-Side Rendering (SSR) is the technique of rendering a React component tree on the server and sending the resulting HTML to the browser. The browser displays the HTML immediately (no blank-screen flicker), then React “hydrates” it — attaching event listeners and making it interactive.
How SSR works:
Client requests URL
↓
Node.js server renders React → HTML string
↓
Server sends fully-formed HTML to browser
↓
Browser displays HTML (fast First Contentful Paint)
↓
React JavaScript bundle loads
↓
React "hydrates" the HTML (attaches event handlers)
↓
Page is fully interactive
React APIs for SSR:
// react-dom/server
import { renderToString } from 'react-dom/server';
import { hydrateRoot } from 'react-dom/client';
// Server side
const html = renderToString(<App />);
// Client side (hydration)
hydrateRoot(document.getElementById('root'), <App />);
SSR with Next.js:
Next.js is the most popular way to use SSR with React.
// pages/products/[id].jsx (Next.js Pages Router)
export async function getServerSideProps({ params }) {
const product = await fetchProduct(params.id);
return { props: { product } };
}
export default function ProductPage({ product }) {
return <h1>{product.name}</h1>;
}
SSR vs CSR vs SSG:
| SSR | CSR (SPA) | SSG | |
|---|---|---|---|
| Rendering | Server, per request | Browser, client-side | Build time, static files |
| SEO | Excellent | Poor (needs workarounds) | Excellent |
| First load speed | Fast (HTML ready) | Slow (blank page + JS) | Fastest |
| Data freshness | Real-time | Real-time (via API) | Stale until rebuild |
| Server cost | Higher | Lower | Lowest |
| Use case | E-commerce, dashboards | Web apps, admin panels | Blogs, marketing sites |
When to use SSR:
- SEO-critical pages (product pages, news, blogs).
- Personalized content that changes per user/request.
- Pages that require real-time data.
- Improving Largest Contentful Paint (LCP) and Core Web Vitals.
Q. How to migrate a React application to a newer version?
Upgrading a React application between major versions requires a structured approach to avoid regressions.
General Migration Steps:
1. Read the Release Notes and Migration Guide
Always check the official React changelog and the migration guide for the target version. Breaking changes are documented there.
2. Upgrade incrementally
# Check for outdated packages
npm outdated
# Upgrade React and ReactDOM together
npm install react@latest react-dom@latest
# Upgrade related tooling
npm install @types/react@latest @types/react-dom@latest # TypeScript
3. Use the official codemods
React provides automated code transformers via react-codemod:
npx react-codemod <transform-name> <path>
# Common transforms:
npx react-codemod update-react-imports . # Remove unused React imports
npx react-codemod rename-unsafe-lifecycles . # Rename UNSAFE_ methods
4. Common changes by version:
| Migration | Key Changes |
|---|---|
| v15 → v16 | Fiber rewrite; componentDidCatch added |
| v16 → v17 | New JSX transform (no import React needed); event delegation moved to root |
| v17 → v18 | createRoot() replaces ReactDOM.render(); concurrent features; automatic batching |
| v18 → v19 | Server Actions, use() hook, ref as prop, improved error reporting |
5. Replace deprecated APIs:
// React 18: replace ReactDOM.render
// ❌ Old
ReactDOM.render(<App />, document.getElementById('root'));
// ✅ New
import { createRoot } from 'react-dom/client';
createRoot(document.getElementById('root')).render(<App />);
6. Address breaking changes and test
- Run your full test suite after each incremental upgrade.
- Fix TypeScript type errors.
- Test SSR hydration if applicable.
- Monitor
console.errorfor deprecation warnings in strict mode.
Migrating a Monorepo:
- Upgrade shared React version in the root
package.jsonor workspace config. - Ensure all packages use the same React version (duplicate React instances cause bugs).
- Use
peerDependenciescorrectly in reusable component packages.
Q. What is the difference between Enzyme and React Testing Library (RTL)?
Enzyme (by Airbnb) and React Testing Library (RTL) (by Kent C. Dodds / Testing Library) are the two most popular testing utilities for React components, but they take fundamentally different approaches.
Enzyme:
- Tests component implementation details — you can inspect state, call lifecycle methods directly, and access internal methods.
- Focuses on the component tree and its internal structure.
- Uses
.shallow()(shallow rendering),.mount()(full DOM), or.render()(static HTML). - Encourages testing how a component works internally.
// Enzyme example
import { shallow } from 'enzyme';
const wrapper = shallow(<Counter />);
wrapper.find('button').simulate('click');
expect(wrapper.state('count')).toBe(1); // Tests internal state
React Testing Library (RTL):
- Tests user behavior — it queries the DOM the way a user would interact with it.
- Discourages testing implementation details (no access to internal state or methods).
- Built on top of
@testing-library/dom; uses queries likegetByText,getByRole,getByLabelText. - Encourages testing what the user sees and does.
// RTL example
import { render, screen, fireEvent } from '@testing-library/react';
render(<Counter />);
fireEvent.click(screen.getByRole('button', { name: /increment/i }));
expect(screen.getByText('Count: 1')).toBeInTheDocument(); // Tests visible output
Comparison:
| Feature | Enzyme | RTL |
|---|---|---|
| Philosophy | Implementation details | User behavior |
| Shallow rendering | ✅ Yes | ❌ No (full render only) |
| Access to state | ✅ Yes | ❌ No (by design) |
| Query style | CSS selectors, class names | Role, text, label, placeholder |
| React 18 support | Limited / unofficial | ✅ Official |
| Active maintenance | Minimal | ✅ Actively maintained |
| Recommended for new projects | ❌ No | ✅ Yes |
Recommendation:
- Use RTL for all new React projects. It aligns with best practices, is actively maintained, and produces more resilient tests.
- Enzyme may still be used in legacy codebases but should be migrated away from.
Q. How do you measure and enforce code coverage in a React project?
Code coverage measures how much of your source code is executed during automated tests. It's reported across four dimensions: Statements, Branches, Functions, and Lines.
Running Coverage with Vitest (recommended with Vite):
npm install --save-dev @vitest/coverage-v8
# vitest.config.js
export default {
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
thresholds: {
statements: 80,
branches: 75,
functions: 80,
lines: 80,
},
exclude: ['node_modules', 'src/**/*.stories.*', 'src/main.jsx'],
},
},
};
# Run coverage
npx vitest run --coverage
Running Coverage with Jest (CRA / Jest-based setups):
# Run tests and collect coverage
npx react-scripts test --coverage --watchAll=false
# Or directly with Jest
npx jest --coverage
Coverage output:
----------|---------|----------|---------|---------|
File | % Stmts | % Branch | % Funcs | % Lines |
----------|---------|----------|---------|---------|
Counter.jsx | 95.24 | 87.50 | 100.00 | 95.24 |
----------|---------|----------|---------|---------|
Code Quality Measures:
Beyond coverage, React projects benefit from these quality tools:
| Tool | Purpose |
|---|---|
ESLint + eslint-plugin-react |
Static analysis: unused vars, React rules |
| eslint-plugin-react-hooks | Enforces Rules of Hooks |
| Prettier | Consistent code formatting |
| TypeScript | Static type checking |
| Husky + lint-staged | Pre-commit hooks to run linters/tests |
| SonarQube / SonarCloud | Continuous code quality and security scanning |
| Codecov / Coveralls | Coverage tracking and PR gate checks |
Example: Husky pre-commit hook
// package.json
{
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"]
}
}
Q. What are client-side logging and monitoring tools for React applications?
Client-side monitoring captures errors, performance metrics, and user behavior in the browser, helping you diagnose issues in production that you can't reproduce locally.
1. Error Monitoring — Sentry
The most widely used React error monitoring tool. Captures JavaScript exceptions, component stack traces, and release-level tracking.
npm install @sentry/react
// main.jsx
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: 'https://<key>@sentry.io/<project>',
integrations: [Sentry.browserTracingIntegration()],
tracesSampleRate: 0.2, // 20% of transactions
release: import.meta.env.VITE_APP_VERSION,
});
// Wrap your app with the ErrorBoundary
root.render(
<Sentry.ErrorBoundary fallback={<p>An error occurred</p>}>
<App />
</Sentry.ErrorBoundary>
);
2. Performance Monitoring — Web Vitals
React 18+ ships with built-in support for reporting Core Web Vitals:
import { onCLS, onFID, onLCP } from 'web-vitals';
function sendToAnalytics(metric) {
console.log(metric); // or POST to your analytics endpoint
}
onCLS(sendToAnalytics);
onFID(sendToAnalytics);
onLCP(sendToAnalytics);
3. Session Recording — LogRocket / FullStory
Records user sessions, network requests, and Redux state changes so you can replay what happened before a crash.
npm install logrocket
import LogRocket from 'logrocket';
LogRocket.init('your-app/project-id');
4. Application Performance Monitoring (APM)
| Tool | Focus |
|---|---|
| Sentry | Error tracking + performance |
| Datadog RUM | Real User Monitoring, traces |
| New Relic Browser | Full-stack APM |
| LogRocket | Session replay + Redux logging |
| Grafana / OpenTelemetry | Custom metrics, self-hosted |
5. Console Logging Best Practices
// Use structured logging in development
if (process.env.NODE_ENV === 'development') {
console.log('[ComponentName]', { state, props });
}
// Never log sensitive data (tokens, passwords, PII)
// Use log levels: debug/info/warn/error
Key metrics to monitor:
- JavaScript errors — unhandled exceptions, promise rejections.
- Core Web Vitals — LCP (load), FID/INP (interactivity), CLS (layout shift).
- API latency — slow or failed network requests.
- User flows — drop-off points and error-triggering actions.
# 23. REACT TYPESCRIPT
Q. How to type React functional components with TypeScript?
TypeScript adds static typing to React components, catching type errors at compile time rather than runtime. There are two primary approaches for typing functional components.
1. Typing Props with an Interface:
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean; // Optional prop
variant?: 'primary' | 'secondary';
}
function Button({ label, onClick, disabled = false, variant = 'primary' }: ButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
className={`btn btn-${variant}`}
>
{label}
</button>
);
}
// Usage
<Button label="Submit" onClick={() => console.log('clicked')} />
2. Using React.FC (Function Component):
import React from 'react';
interface CardProps {
title: string;
children: React.ReactNode;
}
const Card: React.FC<CardProps> = ({ title, children }) => {
return (
<div className="card">
<h2>{title}</h2>
<div>{children}</div>
</div>
);
};
Note:
React.FCis considered controversial because it implicitly addschildrento props (removed in React 18 types) and may hide return type flexibility. Prefer explicitly typed function signatures for new code.
3. Typing useState:
const [count, setCount] = useState<number>(0);
const [user, setUser] = useState<User | null>(null);
const [items, setItems] = useState<string[]>([]);
4. Typing useRef:
const inputRef = useRef<HTMLInputElement>(null);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
Q. What is the difference between interface and type for React props in TypeScript?
Both interface and type can describe the shape of React props, but they differ in capabilities and intent.
| Feature | interface |
type |
|---|---|---|
| Declaration merging | ✅ Yes | ❌ No |
| Union / intersection | ❌ Limited | ✅ Yes |
| Extends keyword | ✅ Yes | ✅ (via &) |
| Primitive aliases | ❌ No | ✅ Yes |
| Mapped types | ❌ No | ✅ Yes |
When to use interface (recommended for props):
// Extendable — good for component prop hierarchies
interface BaseProps {
id: string;
className?: string;
}
interface ButtonProps extends BaseProps {
label: string;
onClick: () => void;
}
When to use type (recommended for unions/computed types):
// Union types — only possible with type
type Status = 'idle' | 'loading' | 'success' | 'error';
// Intersection types
type AdminProps = UserProps & { adminLevel: number };
// Conditional types
type IsString<T> = T extends string ? true : false;
Best Practice: Use interface for props and component types. Use type for unions, intersections, and utility types.
Q. How to use generics in React components with TypeScript?
Generics allow React components to work with different data types while remaining type-safe.
Generic List Component:
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
keyExtractor: (item: T) => string;
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
return (
<ul>
{items.map((item) => (
<li key={keyExtractor(item)}>{renderItem(item)}</li>
))}
</ul>
);
}
// Usage - TypeScript infers T as User
interface User { id: string; name: string; }
<List<User>
items={users}
renderItem={(user) => <span>{user.name}</span>}
keyExtractor={(user) => user.id}
/>
Generic Custom Hook:
function useFetch<T>(url: string): { data: T | null; loading: boolean; error: string | null } {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then((json: T) => { setData(json); setLoading(false); })
.catch(err => { setError(err.message); setLoading(false); });
}, [url]);
return { data, loading, error };
}
// Usage
const { data: users, loading } = useFetch<User[]>('/api/users');
Q. How to type event handlers in React with TypeScript?
React provides specific event types for each DOM event, ensuring correct handler signatures.
import React, { ChangeEvent, FormEvent, MouseEvent, KeyboardEvent } from 'react';
function Form() {
// Input change event
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
console.log(event.target.value);
};
// Textarea change event
const handleTextArea = (event: ChangeEvent<HTMLTextAreaElement>) => {
console.log(event.target.value);
};
// Select change event
const handleSelect = (event: ChangeEvent<HTMLSelectElement>) => {
console.log(event.target.value);
};
// Form submit event
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
// process form...
};
// Button click event
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
console.log(event.currentTarget.id);
};
// Keyboard event
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
// handle Enter key
}
};
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} onKeyDown={handleKeyDown} />
<button onClick={handleClick} id="submit-btn" type="submit">Submit</button>
</form>
);
}
Common React TypeScript Event Types:
| Event | Type |
|---|---|
| Click | MouseEvent<HTMLButtonElement> |
| Input change | ChangeEvent<HTMLInputElement> |
| Form submit | FormEvent<HTMLFormElement> |
| Keyboard | KeyboardEvent<HTMLElement> |
| Focus/Blur | FocusEvent<HTMLElement> |
| Drag | DragEvent<HTMLElement> |
Q. How to type useReducer with TypeScript?
// Define state and action types
interface CounterState {
count: number;
step: number;
}
type CounterAction =
| { type: 'INCREMENT' }
| { type: 'DECREMENT' }
| { type: 'RESET' }
| { type: 'SET_STEP'; payload: number };
// Reducer with full type safety
function counterReducer(state: CounterState, action: CounterAction): CounterState {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + state.step };
case 'DECREMENT':
return { ...state, count: state.count - state.step };
case 'RESET':
return { ...state, count: 0 };
case 'SET_STEP':
return { ...state, step: action.payload };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0, step: 1 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
<button onClick={() => dispatch({ type: 'SET_STEP', payload: 5 })}>Set Step 5</button>
</div>
);
}
Q. How to type the Context API with TypeScript?
import React, { createContext, useContext, useState } from 'react';
interface AuthContextType {
user: User | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
isAuthenticated: boolean;
}
interface User {
id: string;
name: string;
email: string;
}
// Create context with undefined as default (forces null check)
const AuthContext = createContext<AuthContextType | undefined>(undefined);
// Custom hook with type guard
export function useAuth(): AuthContextType {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
// Provider component
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const login = async (email: string, password: string) => {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
const userData: User = await response.json();
setUser(userData);
};
const logout = () => setUser(null);
return (
<AuthContext.Provider value=>
{children}
</AuthContext.Provider>
);
}
# 24. REACT ACCESSIBILITY
Q. What is React Accessibility (a11y) and why does it matter?
Accessibility (a11y) ensures web applications are usable by people with disabilities, including those using screen readers, keyboard navigation, or other assistive technologies. React supports all standard HTML accessibility attributes, which use camelCase in JSX.
Key Principles (WCAG):
- Perceivable - Information must be presentable in ways users can perceive
- Operable - UI must be navigable via keyboard and other input methods
- Understandable - Content and UI must be understandable
- Robust - Content must work with assistive technologies
Basic Examples:
// ✅ Provide alt text for images
<img src="logo.png" alt="Company Logo" />
// ✅ Use semantic HTML elements
<nav aria-label="Main Navigation">
<ul>
<li><a href="/home">Home</a></li>
</ul>
</nav>
// ✅ Associate labels with form controls
<label htmlFor="email">Email Address</label>
<input id="email" type="email" aria-required="true" />
// ✅ Use buttons for interactive actions, not divs
<button onClick={handleSubmit} type="button">Submit</button>
// ❌ Avoid non-semantic interactive elements
<div onClick={handleSubmit}>Submit</div> // Not keyboard accessible
Tools for testing accessibility:
- eslint-plugin-jsx-a11y - Lint accessibility issues at build time
- axe-core / @axe-core/react - Runtime accessibility testing
- React Testing Library - Tests from a user perspective
- Lighthouse - Chrome DevTools accessibility audit
Q. How to implement keyboard navigation in React?
Keyboard navigation is critical for users who cannot use a mouse. React handles keyboard events via onKeyDown, onKeyUp, and onKeyPress (deprecated).
1. Managing Focus Manually:
import { useRef, useEffect } from 'react';
function Modal({ isOpen, onClose, children }) {
const closeButtonRef = useRef(null);
// Move focus to close button when modal opens
useEffect(() => {
if (isOpen) {
closeButtonRef.current?.focus();
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div role="dialog" aria-modal="true" aria-labelledby="modal-title">
<h2 id="modal-title">Dialog Title</h2>
{children}
<button ref={closeButtonRef} onClick={onClose}>
Close
</button>
</div>
);
}
2. Focus Trap Inside a Modal:
function useFocusTrap(isActive) {
const containerRef = useRef(null);
useEffect(() => {
if (!isActive) return;
const focusableSelectors = [
'a[href]', 'button:not([disabled])', 'input:not([disabled])',
'textarea', 'select', '[tabindex]:not([tabindex="-1"])'
].join(', ');
const container = containerRef.current;
const focusableElements = container?.querySelectorAll(focusableSelectors);
const firstElement = focusableElements?.[0];
const lastElement = focusableElements?.[focusableElements.length - 1];
const handleKeyDown = (e) => {
if (e.key !== 'Tab') return;
if (e.shiftKey) {
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement?.focus();
}
} else {
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement?.focus();
}
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [isActive]);
return containerRef;
}
3. Keyboard-accessible custom component:
function MenuItem({ label, onClick }) {
return (
<li
role="menuitem"
tabIndex={0}
onClick={onClick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onClick();
}
}}
>
{label}
</li>
);
}
Q. What are ARIA attributes and how are they used in React?
ARIA (Accessible Rich Internet Applications) attributes add semantic meaning to elements to aid assistive technologies. In React, all ARIA attributes are supported and use their standard hyphenated names.
Common ARIA Attributes:
// aria-label: Provides an accessible name when visible label is unavailable
<button aria-label="Close dialog" onClick={onClose}>✕</button>
// aria-labelledby: Points to an element that labels the current element
<section aria-labelledby="section-heading">
<h2 id="section-heading">Products</h2>
</section>
// aria-describedby: Points to a description element
<input
type="password"
aria-describedby="password-hint"
/>
<p id="password-hint">Must be at least 8 characters</p>
// aria-live: Announces dynamic content changes to screen readers
<div aria-live="polite" aria-atomic="true">
{statusMessage}
</div>
// aria-expanded: Indicates whether a collapsible element is open
<button aria-expanded={isMenuOpen} aria-controls="menu-list">
Menu
</button>
<ul id="menu-list" hidden={!isMenuOpen}>
<li>Item 1</li>
</ul>
// aria-invalid and aria-errormessage: Form validation feedback
<input
aria-invalid={!!errors.email}
aria-errormessage="email-error"
/>
{errors.email && <span id="email-error" role="alert">{errors.email}</span>}
// aria-hidden: Hide decorative elements from screen readers
<span aria-hidden="true">★★★★☆</span>
<span className="sr-only">4 out of 5 stars</span>
Landmark Roles:
<header role="banner">...</header>
<nav role="navigation" aria-label="Primary">...</nav>
<main role="main">...</main>
<aside role="complementary">...</aside>
<footer role="contentinfo">...</footer>
Q. How to make a React application fully accessible for screen readers?
1. Manage page titles dynamically:
import { useEffect } from 'react';
function usePageTitle(title) {
useEffect(() => {
document.title = title;
}, [title]);
}
function ProductPage({ product }) {
usePageTitle(`${product.name} - My Store`);
return <div>...</div>;
}
2. Skip navigation link:
// Allows keyboard users to skip repetitive navigation
function SkipToMain() {
return (
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:absolute focus:top-0 focus:left-0"
>
Skip to main content
</a>
);
}
function App() {
return (
<>
<SkipToMain />
<header>...</header>
<main id="main-content">...</main>
</>
);
}
3. Announce route changes to screen readers:
import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';
function RouteAnnouncer() {
const location = useLocation();
const announceRef = useRef(null);
useEffect(() => {
announceRef.current.textContent = `Navigated to ${document.title}`;
}, [location]);
return (
<div
ref={announceRef}
aria-live="assertive"
aria-atomic="true"
className="sr-only"
/>
);
}
4. Screen reader only utility class (CSS):
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
# 25. REACT SECURITY & AUTHENTICATION
Q. How to implement JWT authentication in React?
JWT (JSON Web Token) authentication involves storing a token after login and sending it with every API request.
1. Login and Token Storage:
// AuthService.js
const API_URL = '/api';
export async function login(email, password) {
const response = await fetch(`${API_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (!response.ok) throw new Error('Login failed');
const { token, user } = await response.json();
// Store token in memory (most secure) or sessionStorage
// Avoid localStorage for sensitive tokens due to XSS risk
sessionStorage.setItem('authToken', token);
return user;
}
export function getToken() {
return sessionStorage.getItem('authToken');
}
export function logout() {
sessionStorage.removeItem('authToken');
}
// Axios interceptor to attach token to every request
import axios from 'axios';
axios.interceptors.request.use((config) => {
const token = getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
2. Auth Context:
import { createContext, useContext, useState, useCallback } from 'react';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = useCallback(async (email, password) => {
const userData = await loginService(email, password);
setUser(userData);
}, []);
const logout = useCallback(() => {
logoutService();
setUser(null);
}, []);
return (
<AuthContext.Provider value=>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => useContext(AuthContext);
Q. How to implement protected routes in React Router?
Protected routes redirect unauthenticated users away from private pages.
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { useAuth } from './AuthContext';
// Protected Route component
function ProtectedRoute({ allowedRoles }) {
const { isAuthenticated, user } = useAuth();
const location = useLocation();
if (!isAuthenticated) {
// Redirect to login, preserving intended destination
return <Navigate to="/login" state= replace />;
}
if (allowedRoles && !allowedRoles.includes(user.role)) {
return <Navigate to="/unauthorized" replace />;
}
return <Outlet />;
}
// App Router Setup
function App() {
return (
<Routes>
{/* Public routes */}
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegisterPage />} />
{/* Protected routes - any authenticated user */}
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
</Route>
{/* Role-based protected routes */}
<Route element={<ProtectedRoute allowedRoles={['admin']} />}>
<Route path="/admin" element={<AdminPanel />} />
</Route>
</Routes>
);
}
// LoginPage redirects back to intended destination
function LoginPage() {
const { login } = useAuth();
const navigate = useNavigate();
const location = useLocation();
const from = location.state?.from?.pathname || '/dashboard';
const handleSubmit = async (e) => {
e.preventDefault();
await login(email, password);
navigate(from, { replace: true });
};
}
Q. What are XSS security best practices in React?
Cross-Site Scripting (XSS) injects malicious scripts into web pages. React has built-in XSS protection, but developers can still introduce vulnerabilities.
React's built-in protection:
React automatically escapes all string values rendered in JSX. The following is safe:
const userInput = '<script>alert("xss")</script>';
// React renders this as text, not HTML — safe ✅
return <div>{userInput}</div>;
Dangerous: dangerouslySetInnerHTML:
// ❌ NEVER render unsanitized user content as HTML
function UserBio({ bio }) {
return <div dangerouslySetInnerHTML= />;
}
// ✅ Sanitize with DOMPurify before rendering as HTML
import DOMPurify from 'dompurify';
function UserBio({ bio }) {
const sanitizedBio = DOMPurify.sanitize(bio, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
ALLOWED_ATTR: ['href'],
});
return <div dangerouslySetInnerHTML= />;
}
Avoid javascript: URLs:
// ❌ Vulnerable to XSS via javascript: URL
function UserLink({ href, label }) {
return <a href={href}>{label}</a>;
}
// ✅ Validate and allow only safe URL schemes
function SafeLink({ href, label }) {
const isSafe = href.startsWith('https://') || href.startsWith('http://') || href.startsWith('/');
if (!isSafe) return <span>{label}</span>;
return <a href={href} rel="noopener noreferrer">{label}</a>;
}
Content Security Policy (CSP):
<!-- Set CSP header in index.html or via server headers -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" />
Q. How to handle sensitive data and environment variables securely in React?
// ✅ Use environment variables for API keys and configuration
// .env file (never commit to version control)
// REACT_APP_API_URL=https://api.example.com
// VITE_API_KEY=your-public-api-key
// Access in React (Vite)
const apiUrl = import.meta.env.VITE_API_URL;
// Access in React (Create React App)
const apiUrl = process.env.REACT_APP_API_URL;
// ⚠️ Warning: ALL environment variables prefixed with REACT_APP_ or VITE_
// are bundled into the client-side JavaScript — they are NOT secret.
// Never store private keys, database passwords, or secrets this way.
// ✅ Keep truly sensitive operations on the server
async function getUserData(userId) {
// The server handles the DB call and authentication
const response = await fetch(`/api/users/${userId}`, {
headers: { Authorization: `Bearer ${getToken()}` },
});
return response.json();
}
// ✅ Never log sensitive information
// ❌ console.log('User password:', password);
// ✅ console.log('Login attempt for user:', email);
Token Security Best Practices:
| Storage | XSS Risk | CSRF Risk | Use Case |
|---|---|---|---|
localStorage |
High | Low | Avoid for auth tokens |
sessionStorage |
High | Low | Short-lived sessions |
HttpOnly Cookie |
None | High (mitigated with SameSite) | Recommended for auth |
| Memory (JS variable) | Low | Low | Best for high-security apps |
Q. How to implement OAuth 2.0 / Social Login in React?
// Using a library like react-oauth/google or Auth0
// Example: Google OAuth with @react-oauth/google
import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google';
import { jwtDecode } from 'jwt-decode';
function App() {
return (
<GoogleOAuthProvider clientId={import.meta.env.VITE_GOOGLE_CLIENT_ID}>
<LoginPage />
</GoogleOAuthProvider>
);
}
function LoginPage() {
const { login } = useAuth();
const handleGoogleSuccess = async (credentialResponse) => {
// Decode the Google JWT to get user info
const decoded = jwtDecode(credentialResponse.credential);
// Send the credential to your backend for verification
const response = await fetch('/api/auth/google', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ credential: credentialResponse.credential }),
});
const { token, user } = await response.json();
login(token, user);
};
return (
<div>
<h1>Sign In</h1>
<GoogleLogin
onSuccess={handleGoogleSuccess}
onError={() => console.log('Login Failed')}
/>
</div>
);
}
Q. What are the different login/authentication types used in React applications?
React applications can implement several authentication strategies depending on security requirements and backend architecture.
1. Cookie-Based Authentication (Session Auth)
The server creates a session and sets a Set-Cookie header. The browser stores the cookie and sends it automatically with every subsequent request.
// Login: server sets httpOnly cookie
async function login(username, password) {
await fetch('/api/login', {
method: 'POST',
credentials: 'include', // Required to send/receive cookies cross-origin
body: JSON.stringify({ username, password }),
headers: { 'Content-Type': 'application/json' },
});
}
// All subsequent requests include the cookie automatically
fetch('/api/profile', { credentials: 'include' });
2. Token-Based Authentication (JWT)
The server issues a signed JSON Web Token (JWT) on login. The client stores it (localStorage or memory) and attaches it to every request via the Authorization header.
// Store token (prefer memory or sessionStorage over localStorage)
const token = await loginResponse.json().token;
// Attach to every API request
fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` },
});
3. OAuth 2.0 / Social Login
Users authenticate via a third-party provider (Google, GitHub, Microsoft) and the app receives an access token or authorization code.
- The user is redirected to the provider's login page.
- After consent, the provider redirects back with a code.
- The server exchanges the code for an access token.
Popular libraries: @react-oauth/google, react-github-login, next-auth.
4. Single Sign-On (SSO)
One login grants access to multiple applications within an organization. Common protocols: SAML, OIDC (OpenID Connect), and OAuth 2.0.
// OIDC with a library like @auth0/auth0-react
import { useAuth0 } from '@auth0/auth0-react';
function LoginButton() {
const { loginWithRedirect } = useAuth0();
return <button onClick={loginWithRedirect}>Log In</button>;
}
5. Multi-Factor Authentication (MFA)
Adds a second verification step (OTP, authenticator app, email code) after password entry.
Comparison:
| Type | Storage | CSRF Risk | XSS Risk | Use Case |
|---|---|---|---|---|
| Cookie (httpOnly) | Server session | Medium (needs CSRF token) | Low | Traditional web apps |
| JWT (localStorage) | Client | Low | High | SPAs, mobile APIs |
| JWT (httpOnly cookie) | Client cookie | Medium | Low | Best of both worlds |
| OAuth 2.0 | Provider | Low | Low | Social login |
| SSO / OIDC | Provider/Server | Low | Low | Enterprise apps |
Q. How to handle cookies securely in React applications?
Cookies can be used for authentication tokens, session IDs, or user preferences, but they must be configured correctly to prevent security vulnerabilities.
Secure Cookie Attributes:
| Attribute | Purpose |
|---|---|
HttpOnly |
Prevents JavaScript (document.cookie) from accessing the cookie — mitigates XSS |
Secure |
Cookie is only sent over HTTPS connections |
SameSite=Strict |
Cookie not sent on cross-site requests — prevents CSRF |
SameSite=Lax |
Cookie sent on top-level navigation but not cross-site sub-requests (default in modern browsers) |
Max-Age / Expires |
Controls cookie lifetime |
Path |
Restricts cookie to a specific URL path |
Setting secure cookies on the server (Node.js / Express):
// Server sets the cookie — not React/client-side
res.cookie('token', jwtToken, {
httpOnly: true, // Not accessible via JS
secure: true, // HTTPS only
sameSite: 'strict', // CSRF protection
maxAge: 3600000, // 1 hour in ms
path: '/',
});
Reading cookies in React (non-httpOnly only):
// ⚠️ Only works for cookies WITHOUT httpOnly flag
import Cookies from 'js-cookie'; // npm install js-cookie
// Set a cookie
Cookies.set('theme', 'dark', { expires: 7, secure: true, sameSite: 'strict' });
// Get a cookie
const theme = Cookies.get('theme');
// Delete a cookie
Cookies.remove('theme');
Best practices:
// ✅ DO: Use httpOnly cookies for auth tokens (set server-side)
// ✅ DO: Always use Secure flag in production
// ✅ DO: Set SameSite to Strict or Lax to prevent CSRF
// ✅ DO: Set short expiry times for sensitive tokens
// ✅ DO: Use CSRF tokens for state-changing requests with cookies
// ❌ DON\'t: Store JWT tokens or sensitive data in localStorage (XSS risk)
// ❌ DON\'t: Set cookies without Secure flag in production
// ❌ DON\'t: Use SameSite=None without also setting Secure
// ❌ DON\'t: Store PII or passwords in cookies
CSRF Protection with cookies:
When using cookie-based auth, protect state-changing endpoints with a CSRF token:
// Include CSRF token from meta tag in POST requests
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
fetch('/api/profile', {
method: 'POST',
credentials: 'include',
headers: {
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
# 26. REACT DATA FETCHING LIBRARIES
Q. What is React Query (TanStack Query) and why use it over useEffect?
React Query is a data-fetching and server state management library that eliminates the boilerplate of managing loading, error, and caching states manually.
Without React Query (useEffect approach):
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
setLoading(true);
fetch('/api/users')
.then(res => res.json())
.then(data => { if (!cancelled) { setUsers(data); setLoading(false); } })
.catch(err => { if (!cancelled) { setError(err); setLoading(false); } });
return () => { cancelled = true; };
}, []);
if (loading) return <Spinner />;
if (error) return <Error message={error.message} />;
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
With React Query:
import { useQuery } from '@tanstack/react-query';
const fetchUsers = () => fetch('/api/users').then(res => res.json());
function UserList() {
const { data: users, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 5 * 60 * 1000, // 5 minutes before refetch
gcTime: 10 * 60 * 1000, // 10 minutes cache retention
});
if (isLoading) return <Spinner />;
if (error) return <Error message={error.message} />;
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
Key benefits of React Query:
- Automatic caching - Deduplicates identical requests
- Background refetching - Keeps data fresh automatically
- Optimistic updates - Update UI before server confirms
- Pagination / Infinite queries - Built-in utilities
- Devtools - Visual inspection of query state
- Window focus refetching - Refreshes stale data when user returns to tab
Q. How to perform mutations with React Query?
import { useMutation, useQueryClient } from '@tanstack/react-query';
function CreatePost() {
const queryClient = useQueryClient();
const createPostMutation = useMutation({
mutationFn: (newPost) =>
fetch('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newPost),
}).then(res => res.json()),
// Optimistic update: update the UI immediately
onMutate: async (newPost) => {
await queryClient.cancelQueries({ queryKey: ['posts'] });
const previousPosts = queryClient.getQueryData(['posts']);
queryClient.setQueryData(['posts'], (old) => [...old, { ...newPost, id: Date.now() }]);
return { previousPosts };
},
// On error, roll back to the previous value
onError: (err, newPost, context) => {
queryClient.setQueryData(['posts'], context.previousPosts);
},
// Always refetch after error or success
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] });
},
});
const handleSubmit = (e) => {
e.preventDefault();
createPostMutation.mutate({ title: 'New Post', body: 'Content...' });
};
return (
<form onSubmit={handleSubmit}>
<button type="submit" disabled={createPostMutation.isPending}>
{createPostMutation.isPending ? 'Creating...' : 'Create Post'}
</button>
{createPostMutation.isError && <p>Error: {createPostMutation.error.message}</p>}
</form>
);
}
Q. What is SWR and how does it differ from React Query?
SWR (stale-while-revalidate) is a React hooks library from Vercel for remote data fetching. It follows the HTTP cache invalidation strategy: return cached (stale) data first, then revalidate in the background.
import useSWR from 'swr';
const fetcher = (url) => fetch(url).then(res => res.json());
function UserProfile({ userId }) {
const { data: user, error, isLoading, mutate } = useSWR(
`/api/users/${userId}`,
fetcher,
{
revalidateOnFocus: true, // Revalidate when window regains focus
revalidateOnReconnect: true, // Revalidate on network reconnect
refreshInterval: 30000, // Poll every 30 seconds
dedupingInterval: 2000, // Deduplicate requests within 2 seconds
}
);
if (isLoading) return <Skeleton />;
if (error) return <ErrorState />;
return (
<div>
<h1>{user.name}</h1>
<button onClick={() => mutate()}>Refresh</button>
</div>
);
}
Comparison: SWR vs React Query:
| Feature | SWR | React Query |
|---|---|---|
| Bundle size | Smaller (~4KB) | Larger (~13KB) |
| Mutations | Basic | Advanced (optimistic, rollback) |
| Infinite scroll | useSWRInfinite |
useInfiniteQuery |
| Devtools | No | Yes |
| Caching strategy | Simpler | More configurable |
| Learning curve | Low | Medium |
| Best for | Simple data fetching | Complex server state |
Q. How to implement infinite scrolling with React Query?
import { useInfiniteQuery } from '@tanstack/react-query';
import { useInView } from 'react-intersection-observer';
import { useEffect } from 'react';
const fetchPosts = ({ pageParam = 1 }) =>
fetch(`/api/posts?page=${pageParam}&limit=10`).then(res => res.json());
function InfinitePostList() {
const { ref, inView } = useInView();
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading,
} = useInfiniteQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
initialPageParam: 1,
getNextPageParam: (lastPage, pages) =>
lastPage.hasMore ? pages.length + 1 : undefined,
});
// Trigger next page fetch when the sentinel element comes into view
useEffect(() => {
if (inView && hasNextPage) {
fetchNextPage();
}
}, [inView, hasNextPage, fetchNextPage]);
if (isLoading) return <Spinner />;
return (
<div>
{data.pages.map((page, i) => (
<React.Fragment key={i}>
{page.posts.map((post) => (
<PostCard key={post.id} post={post} />
))}
</React.Fragment>
))}
{/* Sentinel element — triggers fetch when visible */}
<div ref={ref}>
{isFetchingNextPage ? <Spinner /> : hasNextPage ? 'Scroll for more' : 'No more posts'}
</div>
</div>
);
}
# 27. REACT CONCURRENT FEATURES
Q. What is React Concurrent Mode and what problems does it solve?
React Concurrent Mode (now called Concurrent Features in React 18+) is a set of new rendering capabilities that allow React to work on multiple tasks simultaneously, interrupting and resuming rendering work based on priority.
Problems it solves:
- Blocking renders: Previously, once React started rendering a component tree, it could not be interrupted. Long renders blocked the main thread, causing janky UIs.
- Stale UI during data fetching: Shows loading spinners instead of previous stale content.
- Input lag: Heavy renders could delay user input responsiveness.
Key Concurrent Features:
| Feature | Hook / API | Purpose |
|---|---|---|
| Deferred rendering | startTransition |
Mark updates as non-urgent |
| Deferred values | useDeferredValue |
Debounce-like deferral for derived state |
| Data fetching | Suspense |
Declarative loading states |
| Streaming SSR | renderToPipeableStream |
Progressive HTML streaming |
Enabling Concurrent Features (React 18+):
Concurrent features are automatically available when using createRoot:
import { createRoot } from 'react-dom/client';
// ✅ Enables Concurrent Features
const root = createRoot(document.getElementById('root'));
root.render(<App />);
// ❌ Legacy mode — no concurrent features
// ReactDOM.render(<App />, document.getElementById('root'));
Q. What is startTransition and when should you use it?
startTransition marks a state update as non-urgent, allowing React to interrupt it in favor of more urgent updates (like user input). This keeps the UI responsive during expensive re-renders.
Without startTransition — UI feels blocked:
function SearchPage() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const handleChange = (e) => {
setQuery(e.target.value);
// This expensive computation blocks the input from updating smoothly
setResults(filterLargeDataset(e.target.value));
};
return (
<>
<input value={query} onChange={handleChange} />
<ResultsList results={results} />
</>
);
}
With startTransition — input stays responsive:
import { useState, useTransition } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value); // Urgent: update input immediately
startTransition(() => {
// Non-urgent: can be interrupted if user types again
setResults(filterLargeDataset(value));
});
};
return (
<>
<input value={query} onChange={handleChange} />
{isPending ? <Spinner /> : <ResultsList results={results} />}
</>
);
}
When to use startTransition:
- Search/filter results from a large dataset
- Tab switching with heavy content
- Sorting/reordering large lists
- Route transitions with data loading
When NOT to use it:
- Controlled input updates (always urgent)
setTimeout/setIntervalcallbacks- Network requests (use React Query or Suspense instead)
Q. What is automatic batching in React 18 and how is it different from React 17?
Automatic batching groups multiple setState calls into a single re-render for performance, even inside asynchronous operations.
React 17 — batching only in event handlers:
// React 17: Both setters trigger ONE render (inside event handler)
function handleClick() {
setCount(c => c + 1); // No render yet
setFlag(f => !f); // No render yet
// ONE render here
}
// React 17: Each setter triggers its OWN render (inside async)
async function fetchData() {
const data = await fetch('/api/data');
setData(data); // Render 1
setLoading(false); // Render 2 — not batched in React 17!
}
React 18 — automatic batching everywhere:
// React 18: Batched even inside async, setTimeout, native events
async function fetchData() {
const data = await fetch('/api/data');
setData(data); // No render yet
setLoading(false); // ONE render here — batched!
}
setTimeout(() => {
setCount(c => c + 1); // No render yet
setFlag(f => !f); // ONE render here — batched!
}, 1000);
Opting out of batching (rare):
import { flushSync } from 'react-dom';
function handleClick() {
flushSync(() => {
setCount(c => c + 1); // Forces an immediate render
});
// DOM updated here
flushSync(() => {
setFlag(f => !f); // Forces another immediate render
});
}
Q. How does Suspense work for data fetching in React 18?
Suspense allows components to “wait” for something before rendering, showing a fallback UI in the meantime. In React 18, it works seamlessly with data fetching libraries that support it.
Basic Suspense for lazy-loaded components:
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading component...</div>}>
<HeavyComponent />
</Suspense>
);
}
Suspense with React Query:
import { useSuspenseQuery } from '@tanstack/react-query';
// useSuspenseQuery suspends the component until data is ready
function UserProfile({ userId }) {
const { data: user } = useSuspenseQuery({
queryKey: ['user', userId],
queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
});
// No loading/error states needed here — handled by Suspense/ErrorBoundary
return <div>{user.name}</div>;
}
function App() {
return (
<ErrorBoundary fallback={<ErrorMessage />}>
<Suspense fallback={<UserSkeleton />}>
<UserProfile userId="123" />
</Suspense>
</ErrorBoundary>
);
}
Nested Suspense boundaries:
function Dashboard() {
return (
<div>
<Suspense fallback={<HeaderSkeleton />}>
<DashboardHeader />
</Suspense>
<Suspense fallback={<ChartSkeleton />}>
<RevenueChart />
<Suspense fallback={<TableSkeleton />}>
<DataTable />
</Suspense>
</Suspense>
</div>
);
}
Q. What are React Server Components (RSC) and how do they differ from SSR?
React Server Components (RSC) is a paradigm introduced in React 18 and popularized by Next.js App Router that allows individual components to run exclusively on the server — never shipping their JavaScript to the browser.
Core difference: RSC vs SSR
| Aspect | SSR (Server-Side Rendering) | React Server Components |
|---|---|---|
| What runs on server | Entire component tree (one time per request) | Individual marked components (permanently) |
| Client JS bundle | Full React + all components sent to browser | Server component code never sent to browser |
| Interactivity | All components hydrated on client | Only Client Components hydrate |
| Data access | Via API calls or getServerSideProps |
Direct DB/filesystem access inside component |
| Framework | Any React + server setup | Next.js App Router, frameworks with RSC support |
Server vs Client Components:
// app/UserProfile.jsx — Server Component (default in Next.js App Router)
// Runs ONLY on server. Has direct DB access. Zero JS bundle cost.
async function UserProfile({ userId }) {
// Direct database query — no API needed!
const user = await db.users.findById(userId);
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
<LikeButton userId={userId} /> {/* ← Client Component for interactivity */}
</div>
);
}
// app/LikeButton.jsx — Client Component
'use client'; // ← This directive makes it a Client Component
import { useState } from 'react';
function LikeButton({ userId }) {
const [liked, setLiked] = useState(false); // useState: Client Components only
return (
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️ Liked' : '🤍 Like'}
</button>
);
}
Rules of Server Components:
Server Components CAN: Server Components CANNOT:
✅ async/await directly ❌ useState, useReducer, useContext
✅ Direct DB/filesystem access ❌ useEffect, useLayoutEffect
✅ Import server-only packages ❌ Browser APIs (window, document)
✅ Render Client Components ❌ Event handlers (onClick, onChange)
✅ Pass serializable props to clients ❌ Pass non-serializable props (functions)
Benefits of React Server Components:
- Zero-bundle-cost components — server component code never goes to the browser.
- Direct backend access — query databases, read files without an intermediate API layer.
- Automatic code splitting — client/server boundary is explicit.
- Better initial load performance — smaller JavaScript bundles.
- Improved security — secrets, tokens, and queries never exposed to the browser.
Example: Data streaming with Suspense
// app/page.jsx (Server Component)
import { Suspense } from 'react';
import UserPosts from './UserPosts';
export default function Page({ params }) {
return (
<div>
<h1>Profile</h1>
<Suspense fallback={<p>Loading posts...</p>}>
<UserPosts userId={params.id} /> {/* Streams in as data is ready */}
</Suspense>
</div>
);
}
# 28. GRAPHQL WITH REACT
Q. What is Apollo Client and how to use it with React?
Apollo Client is the most popular GraphQL client for React. It handles data fetching, caching, and state management for GraphQL APIs.
Setup:
npm install @apollo/client graphql
// index.jsx - Configure Apollo Client
import { ApolloClient, InMemoryCache, ApolloProvider, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
const httpLink = createHttpLink({ uri: 'https://api.example.com/graphql' });
const authLink = setContext((_, { headers }) => {
const token = sessionStorage.getItem('authToken');
return {
headers: { ...headers, authorization: token ? `Bearer ${token}` : '' },
};
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
root.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);
Q. How to implement GraphQL queries and mutations in React?
Queries with useQuery:
import { useQuery, gql } from '@apollo/client';
const GET_PRODUCTS = gql`
query GetProducts($category: String!, $limit: Int) {
products(category: $category, limit: $limit) {
id
name
price
imageUrl
inStock
}
}
`;
function ProductList({ category }) {
const { loading, error, data, refetch } = useQuery(GET_PRODUCTS, {
variables: { category, limit: 20 },
fetchPolicy: 'cache-and-network', // Use cache, but still fetch from network
});
if (loading) return <ProductSkeleton />;
if (error) return <ErrorMessage error={error} />;
return (
<div>
{data.products.map(product => (
<ProductCard key={product.id} product={product} />
))}
<button onClick={() => refetch()}>Refresh</button>
</div>
);
}
Mutations with useMutation:
import { useMutation, gql } from '@apollo/client';
const ADD_TO_CART = gql`
mutation AddToCart($productId: ID!, $quantity: Int!) {
addToCart(productId: $productId, quantity: $quantity) {
id
items {
productId
quantity
}
total
}
}
`;
function AddToCartButton({ productId }) {
const [addToCart, { loading, error }] = useMutation(ADD_TO_CART, {
// Update the cache after mutation
update(cache, { data: { addToCart } }) {
cache.modify({
fields: {
cart: () => addToCart,
},
});
},
onCompleted: () => console.log('Added to cart!'),
onError: (error) => console.error('Error:', error),
});
return (
<button
onClick={() => addToCart({ variables: { productId, quantity: 1 } })}
disabled={loading}
>
{loading ? 'Adding...' : 'Add to Cart'}
</button>
);
}
Q. What is the difference between REST and GraphQL in React applications?
| Aspect | REST | GraphQL |
|---|---|---|
| Data fetching | Multiple endpoints | Single endpoint |
| Over-fetching | Common (gets all fields) | None (request exact fields) |
| Under-fetching | Requires multiple requests | Single request for nested data |
| Type system | Manual (OpenAPI/Swagger) | Built-in schema |
| Caching | HTTP caching (easy) | Custom (normalized cache) |
| Real-time | Polling or WebSockets | Subscriptions (built-in) |
| Learning curve | Low | Medium |
REST approach:
// Needs multiple API calls for related data
const user = await fetch('/api/users/1').then(r => r.json());
const posts = await fetch(`/api/users/1/posts`).then(r => r.json());
const followers = await fetch(`/api/users/1/followers`).then(r => r.json());
GraphQL approach:
// One query for all related data
const DASHBOARD_QUERY = gql`
query UserDashboard($userId: ID!) {
user(id: $userId) {
id
name
email
posts(limit: 5) { id title createdAt }
followers { id name avatar }
}
}
`;
Q. How to use GraphQL Subscriptions for real-time data in React?
import { useSubscription, gql } from '@apollo/client';
const MESSAGE_SUBSCRIPTION = gql`
subscription OnMessageReceived($channelId: ID!) {
messageReceived(channelId: $channelId) {
id
text
sender {
id
name
avatar
}
createdAt
}
}
`;
function LiveChat({ channelId }) {
const [messages, setMessages] = useState([]);
const { loading, error } = useSubscription(MESSAGE_SUBSCRIPTION, {
variables: { channelId },
onData: ({ data }) => {
const newMessage = data.data.messageReceived;
setMessages(prev => [...prev, newMessage]);
},
});
if (loading) return <Spinner />;
if (error) return <ErrorState />;
return (
<div className="chat-container">
{messages.map(msg => (
<MessageBubble key={msg.id} message={msg} />
))}
</div>
);
}
# 29. MICRO-FRONTEND ARCHITECTURE
Q. What is Micro-frontend Architecture and how does it relate to React?
Micro-frontend architecture applies microservices principles to the frontend: the application is divided into smaller, independently developed, deployed, and maintained frontend applications that are composed into a single user experience.
Key Characteristics:
- Independent deployments - Teams deploy their slice without coordinating with others
- Technology agnostic - Each micro-frontend can use a different framework (React, Vue, Angular)
- Isolated teams - Each team owns their domain end-to-end (UI + backend)
- Isolated state - Minimal shared state between micro-frontends
Composition Approaches:
| Approach | Description | Tools |
|---|---|---|
| Build-time integration | Publish as npm packages | Lerna, Nx |
| Run-time via iframes | Maximum isolation, poor UX | Native iframes |
| Run-time via JavaScript | Dynamic script loading | Module Federation |
| Run-time via Web Components | Framework-agnostic | Custom Elements |
| Server-side composition | Edge/server stitching | ESI, Nginx |
Example: Shell application loading a React micro-frontend:
// Shell App (React)
import { lazy, Suspense, useEffect, useState } from 'react';
function MicroFrontend({ name, host }) {
const [loaded, setLoaded] = useState(false);
useEffect(() => {
const scriptId = `mf-script-${name}`;
if (document.getElementById(scriptId)) {
setLoaded(true);
return;
}
const script = document.createElement('script');
script.id = scriptId;
script.src = `${host}/bundle.js`;
script.onload = () => setLoaded(true);
document.head.appendChild(script);
}, [name, host]);
if (!loaded) return <Spinner />;
const Component = window[`render${name}`];
return Component ? <Component /> : null;
}
function App() {
return (
<div>
<MicroFrontend name="Cart" host="https://cart.example.com" />
<MicroFrontend name="Catalog" host="https://catalog.example.com" />
</div>
);
}
Q. What is Module Federation and how does it work with React?
Module Federation (Webpack 5+) allows a React application to dynamically load code from another separately deployed application at runtime, enabling true micro-frontend architecture.
Host Application (shell) — webpack.config.js:
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
// Reference remote apps
cartApp: 'cartApp@https://cart.example.com/remoteEntry.js',
catalogApp: 'catalogApp@https://catalog.example.com/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
}),
],
};
Remote Application (cart) — webpack.config.js:
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'cartApp',
filename: 'remoteEntry.js', // Manifest file consumed by host
exposes: {
'./CartWidget': './src/CartWidget', // Exposed components
'./CartPage': './src/pages/CartPage',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
};
Consuming a remote component:
// In shell application — load CartWidget from the cart micro-frontend
import { lazy, Suspense } from 'react';
const CartWidget = lazy(() => import('cartApp/CartWidget'));
function Header() {
return (
<header>
<nav>...</nav>
<Suspense fallback={<div>Loading cart...</div>}>
<CartWidget />
</Suspense>
</header>
);
}
Shared state between micro-frontends:
// Use a shared event bus (CustomEvent) for cross-MFE communication
// Avoid sharing Redux stores — it creates tight coupling
// Publish event from Cart MFE
window.dispatchEvent(new CustomEvent('cart:updated', {
detail: { itemCount: 3, total: 99.99 }
}));
// Listen in Shell MFE
useEffect(() => {
const handler = (e) => setCartCount(e.detail.itemCount);
window.addEventListener('cart:updated', handler);
return () => window.removeEventListener('cart:updated', handler);
}, []);
Q. What are the challenges of micro-frontend architecture?
1. Shared dependencies and version conflicts:
Challenge: cartApp uses React 18.2, catalogApp uses React 17.
Solution: Use Module Federation\'s shared config with singleton: true
and enforce version ranges in CI/CD pipelines.
2. Consistent UI/UX and Design System:
Challenge: Each team builds their own components differently.
Solution: Publish a shared design system as an npm package or
as a federated module that all MFEs consume.
3. Routing and navigation:
// Challenge: Each MFE has its own router
// Solution: Have the shell own the top-level routes,
// each MFE gets a sub-path
// Shell router
<Routes>
<Route path="/catalog/*" element={<CatalogMFE />} />
<Route path="/cart/*" element={<CartMFE />} />
<Route path="/checkout/*" element={<CheckoutMFE />} />
</Routes>
4. Performance overhead:
Challenge: Loading multiple bundles increases initial load time.
Solution:
- Use shared dependencies (Module Federation shared config)
- Lazy load non-critical MFEs
- Implement aggressive caching headers on remoteEntry.js
5. Authentication & Authorization:
Challenge: Each MFE needs access to auth state.
Solution: Shell authenticates and passes tokens via props,
URL params, or a shared auth library federated module.
Q. What is the difference between Monolith, Monorepo, and Micro-frontend architectures?
These three terms describe different aspects of software architecture. They are often confused but address different concerns: Monolith vs Micro-frontend describes how an application is split at runtime, while Monorepo vs Polyrepo describes how code is organized in repositories.
1. Monolith Architecture
A single, unified application where all features are bundled and deployed together as one unit.
monolith-app/
├── src/
│ ├── features/
│ │ ├── auth/
│ │ ├── dashboard/
│ │ ├── checkout/
│ │ └── reports/
│ └── App.jsx ← Single entry point, single deployment
- Pros: Simple to develop, test, and deploy initially; no network boundaries between features.
- Cons: Scales poorly for large teams; deployment of one feature requires deploying everything; tight coupling.
2. Micro-frontend Architecture
The application is split into independent frontend applications (“micro-frontends”), each owned by a separate team and deployed independently. They are composed in the shell/host application.
shell-app/ ← Orchestrates the MFEs
├── header-mfe/ ← Team A — deploys independently
├── dashboard-mfe/ ← Team B — deploys independently
└── checkout-mfe/ ← Team C — deploys independently
- Pros: Independent deployments, team autonomy, technology flexibility per MFE.
- Cons: Complexity of integration, shared dependency management, performance overhead (multiple bundles).
3. Monorepo
A single Git repository that contains multiple packages/apps. This is a code organization strategy, not a runtime architecture. A monorepo can contain multiple separate apps (micro-frontends or SPAs) or a monolith.
monorepo/
├── apps/
│ ├── shell/ ← React Shell app
│ ├── dashboard-mfe/ ← Micro-frontend
│ └── marketing-site/ ← Next.js site
├── packages/
│ ├── ui-library/ ← Shared components
│ └── utils/ ← Shared utilities
└── package.json ← Workspace root (Turborepo / Nx / pnpm)
- Pros: Shared code is easy to manage; atomic commits across packages; consistent tooling.
- Cons: Larger repository; CI/CD must be configured carefully to avoid rebuilding everything.
4. Polyrepo
Each app/package lives in its own separate repository.
- Pros: Clear ownership, independent CI/CD pipelines, smaller repos.
- Cons: Sharing code requires publishing packages; harder to make atomic cross-repo changes.
Comparison:
| Dimension | Monolith | Micro-frontend | Monorepo | Polyrepo |
|---|---|---|---|---|
| What it describes | Runtime bundling | Runtime composition | Repo organization | Repo organization |
| Deployment | Single deploy | Independent per MFE | Depends on apps inside | Independent per repo |
| Team scalability | Poor | Excellent | Good | Good |
| Code sharing | Easy (same codebase) | Hard (versioned packages) | Easy (shared packages) | Hard |
| Complexity | Low | High | Medium | Medium |
| Common tooling | Any | Module Federation, single-spa | Nx, Turborepo, pnpm workspaces | Git + npm registry |
When to choose:
- Monolith: Small to medium teams, early-stage products, when simplicity matters.
- Micro-frontend: Large organizations with many teams, when independent deployments are critical.
- Monorepo: Any size, when you want to share code between multiple apps while maintaining a single development workflow.
- Polyrepo: Strictly separate teams with different release cadences who rarely share code.
# 30. REAL-TIME DATA WITH REACT
Q. How to implement WebSockets in a React application?
WebSockets provide full-duplex, persistent connections between client and server for real-time communication.
Custom useWebSocket hook:
import { useState, useEffect, useRef, useCallback } from 'react';
function useWebSocket(url) {
const [messages, setMessages] = useState([]);
const [connectionStatus, setConnectionStatus] = useState('connecting');
const wsRef = useRef(null);
const reconnectTimer = useRef(null);
const connect = useCallback(() => {
const ws = new WebSocket(url);
wsRef.current = ws;
ws.onopen = () => {
setConnectionStatus('connected');
console.log('WebSocket connected');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
setMessages(prev => [...prev, data]);
};
ws.onclose = (event) => {
setConnectionStatus('disconnected');
// Auto-reconnect after 3 seconds unless closed intentionally
if (event.code !== 1000) {
reconnectTimer.current = setTimeout(connect, 3000);
}
};
ws.onerror = () => {
setConnectionStatus('error');
ws.close();
};
}, [url]);
useEffect(() => {
connect();
return () => {
clearTimeout(reconnectTimer.current);
wsRef.current?.close(1000, 'Component unmounted');
};
}, [connect]);
const sendMessage = useCallback((data) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify(data));
}
}, []);
return { messages, connectionStatus, sendMessage };
}
// Usage
function LiveChat({ channelId }) {
const { messages, connectionStatus, sendMessage } = useWebSocket(
`wss://api.example.com/chat/${channelId}`
);
return (
<div>
<span>Status: {connectionStatus}</span>
{messages.map((msg, i) => <ChatMessage key={i} message={msg} />)}
<button onClick={() => sendMessage({ text: 'Hello!' })}>Send</button>
</div>
);
}
Q. How to implement real-time updates with Socket.io in React?
Socket.io adds features on top of WebSockets: automatic reconnection, fallback transports, namespaces, and rooms.
npm install socket.io-client
Socket Context for app-wide connection management:
import { createContext, useContext, useEffect, useRef, useState } from 'react';
import { io } from 'socket.io-client';
const SocketContext = createContext(null);
export function SocketProvider({ children }) {
const socketRef = useRef(null);
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
socketRef.current = io(import.meta.env.VITE_API_URL, {
auth: { token: sessionStorage.getItem('authToken') },
transports: ['websocket'],
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
});
socketRef.current.on('connect', () => setIsConnected(true));
socketRef.current.on('disconnect', () => setIsConnected(false));
return () => {
socketRef.current?.disconnect();
};
}, []);
return (
<SocketContext.Provider value=>
{children}
</SocketContext.Provider>
);
}
export const useSocket = () => useContext(SocketContext);
// Chat component using the socket
function ChatRoom({ roomId }) {
const { socket, isConnected } = useSocket();
const [messages, setMessages] = useState([]);
useEffect(() => {
if (!socket) return;
socket.emit('join-room', roomId);
socket.on('message', (msg) => setMessages(prev => [...prev, msg]));
socket.on('user-joined', ({ username }) =>
setMessages(prev => [...prev, { system: true, text: `${username} joined` }])
);
return () => {
socket.emit('leave-room', roomId);
socket.off('message');
socket.off('user-joined');
};
}, [socket, roomId]);
const sendMessage = (text) => {
socket?.emit('send-message', { roomId, text });
};
return (
<div>
<span>{isConnected ? '🟢 Connected' : '🔴 Disconnected'}</span>
{messages.map((msg, i) => <ChatMessage key={i} message={msg} />)}
<MessageInput onSend={sendMessage} />
</div>
);
}
Q. How to implement Server-Sent Events (SSE) in React?
SSE provides one-way, server-to-client streaming over HTTP — ideal for notifications, live feeds, and progress updates where the client only needs to receive data.
import { useState, useEffect } from 'react';
function useSse(url) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [status, setStatus] = useState('connecting');
useEffect(() => {
const eventSource = new EventSource(url, { withCredentials: true });
eventSource.onopen = () => setStatus('connected');
// Listen to the default 'message' event
eventSource.onmessage = (event) => {
setData(JSON.parse(event.data));
};
// Listen to custom named events
eventSource.addEventListener('notification', (event) => {
const notification = JSON.parse(event.data);
setData(notification);
});
eventSource.onerror = () => {
setError('Connection lost');
setStatus('error');
eventSource.close();
};
return () => eventSource.close();
}, [url]);
return { data, error, status };
}
// Live stock ticker using SSE
function StockTicker({ symbol }) {
const { data: stock, status } = useSse(`/api/stocks/${symbol}/stream`);
return (
<div>
<span>{symbol}</span>
{stock ? (
<span className={stock.change >= 0 ? 'positive' : 'negative'}>
${stock.price} ({stock.change > 0 ? '+' : ''}{stock.change}%)
</span>
) : (
<span>{status === 'connecting' ? 'Loading...' : 'Unavailable'}</span>
)}
</div>
);
}
When to use SSE vs WebSockets:
| Criteria | SSE | WebSockets |
|---|---|---|
| Direction | Server → Client only | Bidirectional |
| Protocol | HTTP/HTTPS | ws:// / wss:// |
| Auto-reconnect | Built-in | Manual |
| Proxy/firewall | Easy (HTTP) | Sometimes blocked |
| Use cases | Notifications, live feeds, progress | Chat, gaming, collaboration |
Q. How to debounce API calls in React for search inputs?
Debouncing delays execution until the user stops typing, reducing unnecessary API requests.
import { useState, useEffect, useCallback } from 'react';
// Generic debounce hook
function useDebounce(value, delay = 300) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Search component with debounced API call
function SearchBar() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isSearching, setIsSearching] = useState(false);
const debouncedQuery = useDebounce(query, 400);
useEffect(() => {
if (!debouncedQuery.trim()) {
setResults([]);
return;
}
const controller = new AbortController();
setIsSearching(true);
fetch(`/api/search?q=${encodeURIComponent(debouncedQuery)}`, {
signal: controller.signal,
})
.then(res => res.json())
.then(data => { setResults(data); setIsSearching(false); })
.catch(err => {
if (err.name !== 'AbortError') {
setIsSearching(false);
}
});
// Cancel the fetch if the query changes before it completes
return () => controller.abort();
}, [debouncedQuery]);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
{isSearching && <Spinner />}
<ul>
{results.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
);
}
# 31. TECHNICAL LEAD ROLE
Q. How do you decide on the folder structure and module architecture for a large-scale React application?
A scalable React project structure should reflect the way the team thinks about the domain, not just the file types. There is no single correct answer, but two common strategies are feature-based and layer-based organization.
Feature-based (recommended for large apps):
Group all files related to a single feature together — components, hooks, tests, styles, and API calls live in one folder. This makes it easy to find everything related to a feature, and you can delete or move a feature without hunting across the codebase.
src/
├── features/
│ ├── auth/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── api/
│ │ ├── store/
│ │ └── index.ts ← public API for the feature
│ ├── dashboard/
│ └── products/
├── shared/ ← truly reusable across features
│ ├── components/
│ ├── hooks/
│ └── utils/
├── app/ ← routing, providers, global setup
└── pages/ ← thin route-entry components
Key decisions to make as a lead:
- Enforce barrel files (
index.ts) at feature boundaries to expose a public API and hide internals. - Absolute imports (
@features/auth) over deep relative paths — configure intsconfig.jsonand/orvite.config.ts. - Colocation: Keep tests next to the files they test (
Button.test.tsxbesideButton.tsx). - Shared vs. feature: A component used in two features belongs in
shared/; one used in one feature stays local.
Q. How do you choose between Redux, Context API, Zustand, Jotai, and React Query for state management?
As a tech lead, the answer depends on the type of state being managed, not on habit or personal preference.
| State Type | Description | Recommended Solution |
|---|---|---|
| Server / async state | Data fetched from an API; loading, error, caching | React Query (TanStack Query) or SWR |
| Global UI state | Theme, locale, authenticated user, modal open/closed | React Context (for low-frequency updates) or Zustand |
| Complex shared state | Many interconnected slices, dev-tools needed, time-travel | Redux Toolkit |
| Atomic / fine-grained | Per-item state, derived state, no provider needed | Jotai or Recoil |
| Local component state | Form values, toggle states, counters | useState / useReducer |
Decision heuristic for a lead:
- Always reach for React Query / SWR first for anything that comes from a server. It handles caching, deduplication, background refresh, and optimistic updates out of the box — eliminating the most common Redux use case.
- Use Context for low-frequency global values (auth, theme). Avoid it for high-frequency updates because every consumer re-renders.
- Prefer Zustand over Redux for global UI state in new projects — it has a minimal API, no boilerplate, and works outside of React.
- Keep Redux Toolkit only when the team already has it, needs robust DevTools, or when state logic is genuinely complex (e.g., large e-commerce, financial apps).
Q. How do you conduct effective code reviews for a React team?
Code reviews are one of the highest-leverage activities for a tech lead. The goal is not to find bugs (that's what tests are for) but to transfer knowledge, maintain consistency, and prevent long-term design problems.
What to check in a React PR:
- Correctness: Does the component handle loading, error, and empty states? Are side effects properly cleaned up in
useEffect? - Performance: Are
memo,useCallback, oruseMemoused correctly (or over-used)? Are large lists virtualized? - Accessibility: Are interactive elements keyboard-navigable? Are ARIA attributes correct?
- Naming & readability: Are component, prop, and hook names clear and consistent with the team's conventions?
- Test coverage: Do tests cover the happy path, edge cases, and user interactions — not just snapshots?
- Security: Is user input sanitized? Is
dangerouslySetInnerHTMLavoided or guarded?
Process practices for a lead:
- Set a PR size limit (e.g., 400 lines) and enforce it via guidelines. Large PRs are rarely reviewed well.
- Use automated checks (ESLint, TypeScript, test coverage gates) to handle style and trivial issues so human review focuses on design.
- Give actionable, specific feedback — not “this is bad” but “consider extracting this into a custom hook because it will be needed in the
ProfilePagecomponent too.” - Distinguish blocking vs. non-blocking comments clearly (e.g.,
[nit]prefix for optional suggestions). - Approve and merge promptly — a PR sitting for days kills team velocity and creates merge conflicts.
Q. How do you define and enforce coding standards across a React team?
Standards are only useful if they are automated, documented, and agreed upon — not just enforced by a single person's opinion during code review.
Tooling layer (automated enforcement):
// package.json devDependencies
{
"eslint": "...",
"eslint-plugin-react": "...",
"eslint-plugin-react-hooks": "...", // enforces Rules of Hooks
"eslint-plugin-jsx-a11y": "...", // accessibility rules
"@typescript-eslint/eslint-plugin": "...",
"prettier": "...",
"lint-staged": "...",
"husky": "..."
}
Configure husky + lint-staged to run ESLint and Prettier on every commit. Block merges on CI if linting fails.
Documentation layer:
- Maintain an Architecture Decision Record (ADR) folder (
docs/adr/) where significant decisions (e.g., “We use React Query instead of Redux for server state”) are recorded with context and rationale. - Write a short
CONTRIBUTING.mdcovering naming conventions, component size limits, folder rules, and how to add a new feature.
Social layer:
- Discuss and agree on rules as a team — rules imposed top-down are resented and ignored.
- Run a brief standards retrospective every quarter to remove outdated rules and add new ones.
- Use real examples from your own codebase in the documentation, not abstract guidelines.
Q. How do you define a testing strategy for a React project?
A solid testing strategy follows the Testing Trophy (Kent C. Dodds) — an adaptation of the Testing Pyramid for frontend:
/\
/ \ ← E2E tests (few, slow, high confidence)
/----\
/ \ ← Integration tests (most, medium speed)
/--------\
/ \ ← Unit tests (many, fast, low scope)
/------------\
/ \ ← Static analysis (TypeScript, ESLint — always on)
Practical breakdown for React:
| Layer | Tool | What to test |
|---|---|---|
| Static | TypeScript + ESLint | Type errors, hook rules, a11y |
| Unit | Vitest / Jest | Pure functions, custom hooks (renderHook) |
| Integration | React Testing Library | User interactions, form submission, API mocking with MSW |
| E2E | Playwright / Cypress | Critical user journeys (login, checkout, signup) |
Lead guidelines:
- Test behavior, not implementation. Query the DOM like a user would (
getByRole,getByLabelText) — never test internal state or call component methods directly. - Mock at the network boundary using Mock Service Worker (MSW), not by mocking
fetchor specific modules. - Set a coverage floor (e.g., 80%) but treat coverage as a floor, not a goal — 100% coverage with bad tests is worse than 70% with good ones.
- Prioritize integration tests for feature-level confidence at a reasonable speed.
Q. How do you handle performance investigations and optimizations as a tech lead?
Step 1 — Measure, don't guess. Before any optimization, profile the app with real data.
- React DevTools Profiler: Identifies which components re-render, how often, and how long they take.
- Chrome Performance tab: Reveals long tasks, layout thrashing, and scripting time.
- Lighthouse / Web Vitals: Measures user-facing metrics (LCP, INP, CLS) — the ones that matter most.
why-did-you-renderlibrary: Logs unnecessary re-renders to the console during development.
Step 2 — Fix the right things first.
| Issue | Symptom | Fix |
|---|---|---|
| Large bundle | Slow initial load, high LCP | Code splitting with React.lazy, tree shaking |
| Unnecessary re-renders | Slow interactions | memo, useCallback, state colocation |
| Long lists | Scroll jank | react-window or react-virtual virtualization |
| Slow API responses | Loading spinners everywhere | React Query caching, optimistic updates, streaming |
| Heavy computations | UI freezes | useMemo, useTransition, Web Workers |
Step 3 — Prevent regressions. Add performance budgets to your CI pipeline using tools like bundlesize or Lighthouse CI. This ensures new PRs cannot silently increase the bundle by more than an agreed threshold.
Q. How do you manage React version upgrades and dependency health in a large project?
Upgrade strategy:
- Track the ecosystem — subscribe to the React blog, the
reactGitHub releases, and security advisories (npm audit). - Use a staged upgrade process: upgrade in a feature branch → run the full test suite → test manually in staging → merge.
- Use codemods when available. The React team ships codemods (via
@react-codemod) for breaking changes (e.g., class to hooks, legacy lifecycle renames). - Update React and React DOM together — they must always be the same version.
Dependency hygiene:
# Weekly audit
npm audit # security vulnerabilities
npx npm-check-updates --interactive # shows outdated deps
- Use Dependabot or Renovate to automate dependency update PRs. Configure it to batch patch/minor updates weekly and major updates monthly.
- Before adding a new dependency, evaluate: bundle size impact (use
bundlephobia.com), maintenance status (last commit, open issues), license compatibility. - Set a rule: every new dependency added must be justified in the PR description.
Q. How do you onboard new developers to a React project?
Effective onboarding reduces time-to-productivity and prevents new developers from introducing anti-patterns.
Documentation to prepare before their first day:
README.md— How to install, start, build, and test the project in under 5 commands.ARCHITECTURE.md— Folder structure, key design decisions, data flow diagram, and which patterns are in use (e.g., “we use feature slices; server state via React Query”).CONTRIBUTING.md— Branching strategy, PR process, commit conventions, how to run/write tests.
First week plan:
- Day 1: Set up environment, run the app, read architecture doc.
- Day 2-3: Fix a small, well-defined bug — to navigate real code in context.
- Day 4-5: Deliver a small, scoped feature — to go through the full PR process.
Ongoing support:
- Assign a code buddy (a mid-level or senior developer) for the first month.
- Schedule weekly 1:1s for the first month to surface blockers early.
- Create a shared Slack/Teams channel for questions — answers there benefit the whole team, unlike DMs.
Q. How do you communicate technical debt and architectural improvements to non-technical stakeholders?
Technical debt is invisible to business stakeholders unless you make it visible in terms they care about: speed, risk, and cost.
Strategies:
1. Translate to business impact
Don't say: “Our component tree has excessive prop drilling and lacks a proper data layer.” Say: “Adding any new field to the checkout form currently takes 3–4 days because of how the code is structured. Refactoring this area would reduce that to half a day.”
2. Maintain a tech debt backlog (not just a verbal list)
Keep debt items in the same issue tracker as features. Assign each item a rough impact (development speed, reliability, security) and cost to fix (story points). This makes it easy to prioritize alongside product work.
3. Propose the 20% rule
Negotiate with product management to dedicate ~20% of each sprint to technical improvements. Frame it as: “This keeps the team's velocity stable long-term and prevents the cost of fixing debt from compounding.”
4. Use the “interest” metaphor
Technical debt accrues interest: every new feature built on top of the problematic area costs more. Fixing it now has a one-time cost; leaving it has a recurring cost that grows.
5. Be specific, not general
Stakeholders disengage with vague statements like “the code quality is poor.” Be specific: “We have three production incidents in the last month caused by the same authentication module. Rewriting it will take one sprint and eliminate this entire class of bug.”
Q. How do you migrate a legacy React codebase from class components to functional components and hooks?
Migration should be incremental, not a big-bang rewrite. A big-bang rewrite risks introducing regressions across the entire app simultaneously and stalls feature work for months.
Recommended approach:
1. Establish a baseline
- Ensure the existing codebase has good test coverage before migrating. Tests catch regressions during conversion.
- Add TypeScript gradually if it is not already present — it catches many hook-related bugs at compile time.
2. Set a migration policy
- New code must always use functional components and hooks — no new class components.
- Touched code — if a developer modifies an existing class component for a feature or bug fix, they convert it as part of that PR.
- Dedicated migration sprints for high-traffic or high-risk components.
3. Convert in order of risk (low → high)
- Start with leaf components (no children, simple rendering) — low risk, high count.
- Then container components that manage state.
- Last, complex lifecycle components (those using
componentDidMount,shouldComponentUpdate,getSnapshotBeforeUpdate).
4. Common class → hooks mappings:
// State
this.state = { count: 0 } → const [count, setCount] = useState(0)
// Lifecycle
componentDidMount → useEffect(() => { ... }, [])
componentDidUpdate → useEffect(() => { ... }, [dep])
componentWillUnmount → useEffect(() => { return () => cleanup() }, [])
// Context
static contextType → useContext(MyContext)
// shouldComponentUpdate / PureComponent → React.memo()
5. Track progress
Use a shared spreadsheet or project board listing all class components and their migration status. This keeps the effort visible and prevents it from being deprioritized indefinitely.
Q. How do you set up CI/CD for a React project?
A robust CI/CD pipeline for React automates quality gates and deployment, reducing manual errors and keeping the main branch always deployable.
Typical pipeline stages:
PR Created
│
▼
┌─────────────┐
│ CI Checks │ (fast, ~2–3 min)
│ - Type check (tsc --noEmit)
│ - Lint (ESLint)
│ - Unit + Integration tests (Vitest/Jest)
│ - Bundle size check (bundlesize)
└──────┬──────┘
│ Pass
▼
┌─────────────────────┐
│ Preview Deployment │ (branch-specific URL)
│ - Deploy to staging (Vercel/Netlify preview)
│ - Run E2E tests against preview URL (Playwright)
└──────┬──────────────┘
│ Approved + Merged to main
▼
┌───────────────────────┐
│ Production Deployment │
│ - Build + deploy
│ - Smoke tests
│ - Notify team (Slack)
└───────────────────────┘
Example GitHub Actions workflow (simplified):
name: CI
on: [pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npx tsc --noEmit
- run: npm run lint
- run: npm test -- --coverage --ci
- run: npx bundlesize
Lead responsibilities:
- Define and document the branching strategy (GitFlow vs. trunk-based development).
- Ensure secrets (API keys, tokens) are stored in CI environment variables — never hardcoded.
- Set required status checks on the main branch so PRs cannot be merged without passing CI.
- Monitor build times — if CI exceeds 10 minutes, developers start skipping it.
Q. How do you mentor junior and mid-level React developers on your team?
Mentoring is distinct from managing. As a tech lead, your goal is to increase the team's overall capability, not just your own output.
Structured practices:
- Pair programming: Work alongside junior developers on real tasks — not toy examples. Let them drive while you ask guiding questions. This transfers knowledge in context.
- Deliberate PR feedback: Instead of fixing issues yourself, leave specific, educational comments explaining why something is a problem and how to think about it differently. Link to docs or ADRs.
- Growth-oriented task assignment: Assign tasks that are slightly above a developer's current comfort zone — not so hard they get stuck, not so easy they stagnate.
- Technical presentations: Encourage developers to present topics they’re learning to the team (lightning talks). Teaching is the most effective way to consolidate knowledge.
Feedback approach:
- Give feedback frequently and specifically, not just in annual reviews.
- Follow the Situation–Behavior–Impact (SBI) model: “In yesterday's PR [Situation], you added business logic directly inside the JSX [Behavior], which makes this component harder to test and reuse [Impact]. Let's talk about how to extract that into a hook.”
- Acknowledge progress explicitly — developers often underestimate their own growth.
Common pitfalls to avoid:
- Being the bottleneck: If every decision waits for you, the team doesn't grow. Delegate technical decisions with guidance, then review the outcome.
- Over-engineering examples: Show pragmatic, real-world patterns — not theoretical perfect architecture.
- Ignoring soft skills: Teach developers how to write clear PR descriptions, communicate blockers early, and participate in planning meetings.