React Interview - Advanced
How do you handle data persistence in a React application?
Data persistence in a React application refers to retaining user data or application state even after a page reload or when navigating away from the page. There are several strategies to achieve this, depending on the scope and requirements of the app,
i) Local Storage / Session Storage
Local Storage: Persists even after the browser is closed.
Session Storage: Clears when the browser tab is closed.// Save to local storage localStorage.setItem('user', JSON.stringify(user)); // Retrieve from local storage const user = JSON.parse(localStorage.getItem('user'));useEffect(() => { const savedUser = localStorage.getItem('user'); if (savedUser) { setUser(JSON.parse(savedUser)); } }, []); useEffect(() => { localStorage.setItem('user', JSON.stringify(user)); }, [user]);ii) Cookies
Best for: Small pieces of data, often used for authentication tokensCan be configured to persist and be sent with HTTP requests.
Can be made secure and HTTP-only.
document.cookie = "token=abc123; path=/; max-age=3600";Use libraries like
js-cookiefor easier management.
iii) State Management + Persistence Libraries
Redux + redux-persistRecoil + localStorage integration
Zustand + middleware
import { persist } from 'zustand/middleware'; const useStore = create( persist( (set) => ({ count: 0, increase: () => set((state) => ({ count: state.count + 1 })), }), { name: 'counter-storage' } // stored in localStorage ) );What are render props in React?
Render props is a pattern in React that allows you to share code between components using a function as a prop.
A render prop is a function prop that a component uses to determine what to render.
To make a component more reusable and flexible, by letting the consumer decide what to render based on the internal state or logic of the component.// DataProvider.js import React from 'react'; class DataProvider extends React.Component { state = { data: 'Hello from DataProvider' }; render() { return this.props.render(this.state); } }// App.js <DataProvider render={(state) => ( <h1>{state.data}</h1> )} />i) This pattern is flexible but can get verbose or messy if overused.
ii) Hooks (like
useState,useEffect, and custom hooks) have largely replaced the need for render props in many cases.iii) Still useful in class components or third-party libraries.
What are the different optimization techniques used in React Application?
Optimizing a React application is essential for improving performance, responsiveness, and user experience. Here are the most commonly used optimization techniques in React,
i) Code Splitting
Break your app into smaller bundles so users don't need to load everything at once.
React.lazy + Suspenseconst LazyComponent = React.lazy(() => import('./MyComponent')); <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense>Use dynamic imports with Webpack or tools like Vite.
ii) Memoization
Avoid unnecessary re-renders of components and functions.
React.memo (for components)const MyComponent = React.memo(({ value }) => { return <div>{value}</div>; });useMemo (for expensive calculations)
const computedValue = useMemo(() => expensiveFunction(data), [data]);useCallback (to memoize event handlers)
const handleClick = useCallback(() => { doSomething(); }, []);iii) Virtualization
Render only visible items in large lists/tables. Use libraries like react-window or react-virtualizedimport { FixedSizeList as List } from 'react-window'; <List height={300} itemCount={1000} itemSize={35} width={300} > {({ index, style }) => <div style={style}>Item {index}</div>} </List>iv) Avoid Reconciliation and Re-renders
Use keys correctly in lists.Split large components into smaller ones.
Avoid inline functions/objects as props unless memoized.
v) Bundle Optimization
Use tools like Webpack, Vite, or Parcel to minimize bundles.Enable compression (gzip, Brotli).
Analyze bundle size with tools like source-map-explorer or webpack-bundle-analyzer.
vi) Efficient State Management
Avoid deep prop drilling—use Context API or state libraries (Redux, Zustand).Use React Query, SWR, or Apollo Client for efficient server-state management (caching, revalidation, pagination).
vii) Server-Side Rendering (SSR) / Static Site Generation (SSG)
Use Next.js for SSR or SSG to boost SEO and initial load performance.
viii) Image OptimizationUse responsive images,
srcSet, and WebP.Lazy-load images using
loading="lazy"or a library likereact-lazyload.
ix) Use Production BuildsAlways deploy production builds with
npm run build.It minifies code and removes warnings, dev-only features.
What are Pure Components in React?
A Pure Component in React is a class component that automatically implements a shallow comparison in itsshouldComponentUpdate()lifecycle method.This means a Pure Component will only re-render if its props or state have changed in a meaningful (shallow) way.
import React, { PureComponent } from 'react'; class MyComponent extends PureComponent { render() { return <div>{this.props.name}</div>; } }React compares: Primitive values by value, Objects/arrays by reference
So if you pass a new object or array even with the same content, it will trigger a re-render.
When to Use PureComponent
You have simple props/state that benefit from shallow comparison.You want to avoid unnecessary re-renders.
You’re working in class-based components.
Explain the concept of a Memoization in React
Memoization in React is a performance optimization technique that caches the result of expensive function calls and prevents unnecessary re-computations or re-renders when the inputs haven't changed.
Why Use Memoization?React components can re-render frequently. If,
A component does expensive calculations, or
Receives unchanging props, or
Uses functions/handlers that are recreated every render,
Then memoization helps avoid repeating the same work.
i) useMemo – Memoize a Computationconst expensiveValue = useMemo(() => { return computeExpensiveValue(input); }, [input]);computeExpensiveValueonly re-runs ifinputchanges.Useful for expensive calculations.
ii) useCallback – Memoize a Functionconst handleClick = useCallback(() => { console.log('Clicked!'); }, []);Prevents unnecessary re-creations of a function.
Useful when passing callbacks to child components that rely on referential equality.
iii) React.memo() – Memoize a Component
const MyComponent = React.memo(function MyComponent({ name }) { return <div>Hello, {name}</div>; });Prevents re-rendering unless props actually change.
Similar to
PureComponentbut for functional components.
When Not to Use Memoization
For lightweight calculations—it adds complexity and memory overhead.When dependencies change often (the cache is not effective).
When premature optimization makes code harder to read.
Explain React.memo and React.PureComponent?
BothReact.memoandReact.PureComponentare optimization tools in React that help you prevent unnecessary re-renders by performing shallow comparisons of props.
i) React.memo (Functional Components)
React.memois a higher-order component that memoizes a functional component—meaning React will skip rendering if the props haven't changed.const MyComponent = React.memo((props) => { return <div>{props.name}</div>; });ii) React.PureComponent (Class Components)
React.PureComponentis a base class for class components that implementsshouldComponentUpdate()with a shallow prop and state comparison.class MyComponent extends React.PureComponent { render() { return <div>{this.props.name}</div>; } }This avoids unnecessary re-renders if the props and state are the same (shallow comparison).
What is the React Memo Function?
React.memois a higher-order component (HOC) in React that is used to optimize performance by preventing unnecessary re-renders of functional components.
When a component receives the same props as before,React.memoallows React to skip re-rendering that component, thereby improving efficiency.const MyComponent = React.memo(function MyComponent(props) { return <div>{props.name}</div>; });const Child = React.memo(({ name }) => { console.log('Child rendered'); return <p>Hello, {name}!</p>; }); function Parent() { const [count, setCount] = React.useState(0); return ( <div> <button onClick={() => setCount(count + 1)}>Increment</button> <Child name="React" /> </div> ); }Even when the parent re-renders,
Childwon't re-render unless thenameprop changes.
Shallow ComparisonReact does a shallow comparison of props,
i) Primitive values (numbers, strings, booleans) are compared by value.
ii) Objects and arrays are compared by reference.
So if you pass a new object every time—even with the same data—it will re-render.
When Not to Use React.memoi) For components that render very fast anyway.
ii) If props change every time (e.g., inline objects/functions).
iii) If the added complexity outweighs the performance gain.
When to Use Iti) Component renders frequently with the same props.
ii) Component performance impacts overall app.
iii) You're passing pure, stable props.
Explain the useMemo hook and its usage?
useMemois a React Hook that allows you to memoize the result of a computation, so it only re-runs when its dependencies change. It's used to optimize performance, especially for expensive calculations or to avoid unnecessary re-computations during re-renders.
i) Shallow comparison is used for dependency checking.ii) Only optimize when necessary—overuse adds complexity and may hurt performance.
iii) Do not use
useMemoto replaceuseEffector as a side effect. It's purely for caching returned values.const memoizedValue = useMemo(() => { return computeExpensiveValue(input); }, [input]); // computeExpensiveValue: a function that returns the result you want to cache. // [input]: the dependency array; if none of these values change, the function is not re-run.When to Use useMemo
i) Expensive Calculationsconst expensiveResult = useMemo(() => { let total = 0; for (let i = 0; i < 100000000; i++) { total += i; } return total; }, []);ii) Derived Data from Props or State
const filteredItems = useMemo(() => { return items.filter(item => item.includes(search)); }, [items, search]);iii) Avoid Re-Rendering Child Components Based on Derived Props
const config = useMemo(() => ({ theme: 'dark' }), []); <ChildComponent config={config} />Explain the useCallback hook and its usage?
TheuseCallbackhook in React is used to memoize a function, preventing it from being re-created on every render, unless its dependencies change.This is useful when you,
Pass callback functions to child components,
Want to prevent unnecessary re-renders or re-calculations.
const memoizedCallback = useCallback(() => { // function logic }, [dependencies]); // memoizedCallback: A memoized version of the function. // [dependencies]: The function is re-created only if dependencies change.i)
useCallbackis for memoizing functions.ii) Prevents unnecessary re-creation of functions on each render.
iii) Useful with React.memo, or in event handlers, or callbacks in dependencies.
When to Use useCallback
i) Passing Stable Functions to Children (to avoid re-renders)const Parent = () => { const [count, setCount] = useState(0); const handleClick = useCallback(() => { console.log('Button clicked'); }, []); return <Child onClick={handleClick} />; }; const Child = React.memo(({ onClick }) => { console.log("Child rendered"); return <button onClick={onClick}>Click Me</button>; });ii) Event Handlers Inside Components
const increment = useCallback(() => { setCount(c => c + 1); }, []);Using
useCallbackhere prevents this function from being re-created on every render.
When Not to Use useCallbacki) If the function is not passed to child components.
ii) If the function is not computationally expensive.
iii) Overusing it can increase complexity and hurt performance.
What is the difference between useCallback and useMemo in React?
i) Purpose
useCallback - Memoizes a function
useMemo - Memoizes a computed value
ii) Returns
useCallback - A memoized function
useMemo - A memoized result/value
iii) Usage
useCallback - When you need a stable function reference
useMemo - When you need to avoid re-calculating values
iv) Example
useCallback -useCallback(() => doSomething(), [])
useMemo -useMemo(() => computeValue(), [])
v) Primary Use Case
useCallback - Prevents unnecessary re-renders in child components when passing functions
useMemo - Prevents expensive recalculations on re-render
Rule of Thumbi) Use
useCallbackwhen you're passing a function as a prop and want to prevent unnecessary re-renders of memoized children.ii) Use
useMemowhen you're doing expensive calculations or deriving values from state/props.What is a React Router?
React Router is a standard routing library for React that enables you to create single-page applications (SPAs) with navigation and URL management without refreshing the page.It allows you to:
i) Define routes for different components
ii) Handle dynamic routing
iii) Use browser history for navigation
iv) Keep the UI in sync with the URL
Why Use React Router?In a SPA, everything is rendered on a single HTML page, so you can't rely on traditional page reloads for navigation. React Router:
i) Changes the URL without full page reload
ii) Loads the correct components dynamically
iii) Helps maintain application state and structure
Installationnpm install react-router-domUse
react-router-domfor web apps. Usereact-router-nativefor React Native.i) BrowserRouter
Wraps the entire app and enables routing using the browser's history API.import { BrowserRouter } from 'react-router-dom'; <BrowserRouter> <App /> </BrowserRouter>ii) Routes and Route
Defines path-to-component mappings.import { Routes, Route } from 'react-router-dom'; <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes>iii) Link
Enables navigation without reloading the page.
import { Link } from 'react-router-dom'; <Link to="/about">Go to About</Link>iv) useNavigate
Programmatic navigation in components.
import { useNavigate } from 'react-router-dom'; const navigate = useNavigate(); navigate('/dashboard');v) useParams
To get dynamic route parameters.<Route path="/user/:id" element={<User />} /> // In User component const { id } = useParams();Example
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'; function App() { return ( <BrowserRouter> <nav> <Link to="/">Home</Link> | <Link to="/about">About</Link> </nav> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </BrowserRouter> ); }What are the Significant Components of React Router?
i) <BrowserRouter>Wraps your entire app to enable client-side routing using the HTML5 history API.
Typically used at the root of your application.
import { BrowserRouter } from 'react-router-dom'; <BrowserRouter> <App /> </BrowserRouter>ii) <Routes>
Acts as a container for all your route definitions.Replaces the older
<Switch>component from v5.<Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes>iii) <Route>
Defines a single route.Uses
pathto match the URL andelementto define what to render.<Route path="/contact" element={<Contact />} />Supports nested routes and dynamic parameters (
/user/:id)iv) <Link>
Provides navigation between routes without reloading the page.Similar to an
<a>tag, but uses React Router’s internal navigation.<Link to="/about">About</Link>v) useNavigate()
A hook to programmatically navigate between routes.import { useNavigate } from 'react-router-dom'; const navigate = useNavigate(); navigate('/dashboard');vi) useParams()
A hook to extract URL parameters from the current route.<Route path="/user/:id" element={<User />} /> // In User component const { id } = useParams();vii) useLocation()
Returns the current location object (URL, pathname, search params, etc.)const location = useLocation(); console.log(location.pathname);viii) <Navigate>
Used to redirect users programmatically within the route configuration.<Route path="/login" element={<Navigate to="/dashboard" />} />What are the components of React Router?
In React Router,<Router>components are the core providers that enable routing in different environments. They wrap your application and manage the location, history, and navigation context.React Router provides multiple types of
<Router>components based on different use cases.
i) <BrowserRouter>
Uses the HTML5 History API (pushState,popState)Clean URLs like
/about,/dashboardBest for modern web apps with server-side support for routing
import { BrowserRouter } from 'react-router-dom'; <BrowserRouter> <App /> </BrowserRouter>ii) <HashRouter>
Uses the URL hash (#) to simulate navigationURLs look like
/#/aboutUseful for static sites or environments where the server doesn’t support route handling
import { HashRouter } from 'react-router-dom'; <HashRouter> <App /> </HashRouter>iii) <MemoryRouter>
Stores the navigation history in memoryDoesn’t interact with the browser’s URL
Ideal for testing or non-browser environments (like React Native)
import { MemoryRouter } from 'react-router-dom'; <MemoryRouter> <App /> </MemoryRouter>iv) <StaticRouter>
Used for server-side rendering (SSR)Does not respond to user interaction
Typically used with frameworks like Next.js or custom SSR setups
import { StaticRouter } from 'react-router-dom/server'; <StaticRouter location="/about"> <App /> </StaticRouter>Explain the Difference Between Link and NavLink in React Router.
Both<Link>and<NavLink>are components provided by React Router for navigating between routes without reloading the page. The main difference lies in styling and active state handling.
Link -
i) Purpose
Basic navigation between routes.Renders an anchor (
<a>) tag without page reload.
ii) Usageimport { Link } from 'react-router-dom'; <Link to="/about">About</Link>iii) Features
Simple and lightweight.No built-in way to detect or style the active route.
NavLink -
i) Purpose
Extended version of<Link>that allows styling the active route.Ideal for navigation menus or tabs.
ii) Usageimport { NavLink } from 'react-router-dom'; <NavLink to="/about" className={({ isActive }) => (isActive ? "active" : "")} > About </NavLink>iii) Features
isActive: Automatically detects if the link matches the current URL.Allows conditional styling (e.g., highlighting the current page/tab).
Supports exact matching and nested routes.
When to Use Which?
Use<Link>when you just need to navigate.Use
<NavLink>when you need to highlight the active route (like in a nav bar or sidebar).How to get query parameters in React Router v4?
In React Router v4, there is no built-in API to directly access query parameters (like?name=John&age=25). However, you can easily get them using thelocation.searchproperty combined with theURLSearchParamsWeb API.http://localhost:3000/profile?name=John&age=25Inside Your Component
import React from 'react'; import { withRouter } from 'react-router-dom'; const Profile = ({ location }) => { const query = new URLSearchParams(location.search); const name = query.get('name'); // John const age = query.get('age'); // 25 return ( <div> <h2>Profile</h2> <p>Name: {name}</p> <p>Age: {age}</p> </div> ); }; export default withRouter(Profile); // location.search gives you the query string: "?name=John&age=25". // new URLSearchParams(...) parses it. // .get('key') retrieves the value.Alternate (Functional Component with Hooks)
If you're using React Hooks with React Router v5 (or v4 + hooks workaround)
import { useLocation } from 'react-router-dom'; const useQuery = () => new URLSearchParams(useLocation().search); const Profile = () => { const query = useQuery(); const name = query.get('name'); const age = query.get('age'); return ( <> <h2>{name}</h2> <p>Age: {age}</p> </> ); };React Router v4 focuses on rendering and routing. Query param handling is done using standard JavaScript (
URLSearchParams).How to perform automatic redirect after login?
To perform an automatic redirect after login in a React app (especially using React Router), you typically follow this pattern,
i) Authenticate the userii) Store login state/token
iii) Use
useNavigate()(v6) orhistory.push()(v4/v5) to redirect
Example with React Router v6 (Functional Component)import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; const Login = () => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const navigate = useNavigate(); const handleLogin = (e) => { e.preventDefault(); // Example: validate login (replace with real auth logic) if (username === 'admin' && password === '123') { // ✅ Store auth token or update global state here // 🚀 Redirect to dashboard navigate('/dashboard'); } else { alert('Invalid credentials'); } }; return ( <form onSubmit={handleLogin}> <input value={username} onChange={(e) => setUsername(e.target.value)} placeholder="Username" /> <input value={password} type="password" onChange={(e) => setPassword(e.target.value)} placeholder="Password" /> <button type="submit">Login</button> </form> ); }; export default Login;Example with React Router v4/v5 (
useHistory)import { useHistory } from 'react-router-dom'; const Login = () => { const history = useHistory(); const handleLogin = () => { // After login success history.push('/dashboard'); }; return <button onClick={handleLogin}>Login</button>; };i) Redirect after login -
navigate('/dashboard')orhistory.push()
ii) Keep previous location - Uselocation.state.from
iii) Redirect after login success - Place redirect logic insidehandleLogin()What are the Core Principles of Redux?
Redux is a predictable state container for JavaScript apps, most commonly used with React. It is built around three core principles that make application state management predictable and maintainable.
i) Single Source of Truth
"The state of your whole application is stored in an object tree within a single store."
The entire app’s state is stored in one central object (the Redux store).This makes it easier to track, debug, and persist state.
const initialState = { user: { name: 'John', loggedIn: true }, cart: [{ id: 1, name: 'Apple', quantity: 2 }] };Benefit -
Simplifies debugging and testingEnables easy integration with dev tools and logging
ii) State is Read-Only
"The only way to change the state is to emit an action, an object describing what happened."
You cannot mutate state directly.You must dispatch an action to describe what should happen.
store.dispatch({ type: 'ADD_TODO', payload: { text: 'Learn Redux' } });Benefit -
Makes state mutations traceable and predictableImproves maintainability in large apps
iii) Changes are Made with Pure Functions (Reducers)
"To specify how the state tree is transformed by actions, you write pure reducers."
Reducers are pure functions that: Take current state and action as input and Return new state without side effectsfunction todosReducer(state = [], action) { switch (action.type) { case 'ADD_TODO': return [...state, { text: action.payload.text }]; default: return state; } }Benefit -
Ensures consistency
Makes logic easy to test and reason about
What are the Core Components of Redux?
Redux is centered around managing and updating application state in a predictable way. Its architecture is built on a few core components that work together to maintain a consistent data flow.
i) Store
The central place that holds the entire state of your application.
Key Responsibilities:a) Holds the state object
b) Allows access to state via
getState()c) Allows state to be updated via
dispatch(action)d) Registers listeners with
subscribe()import { createStore } from 'redux'; const store = createStore(reducer);ii) Actions
Plain JavaScript objects that describe what happened.
Key Features:a) Must have a
typefield (string)b) May contain additional payload data
const action = { type: 'ADD_TODO', payload: { text: 'Learn Redux' } };iii) Reducers
Pure functions that take the current state and an action, and return the new state.
Key Rules:a) Must be pure (no side effects)
b) Must not mutate the original state
const todoReducer = (state = [], action) => { switch (action.type) { case 'ADD_TODO': return [...state, { text: action.payload.text }]; default: return state; } }iv) Dispatch
The method used to send actions to the store.
store.dispatch({ type: 'ADD_TODO', payload: { text: 'Buy milk' } });v) Subscribers
Functions that listen for changes in the store.
store.subscribe(() => { console.log('State updated:', store.getState()); });What are the Advantages of Redux Over React?
While React has its own built-in state system (useState,useReducer,Context API), Redux offers a more powerful and structured approach, especially for complex applications. Here's a breakdown of how Redux enhances state management compared to vanilla React,
i) Centralized Global State (Single Source of Truth)
React Only:
State is often scattered across multiple components.Redux:
All application state is stored in a central store, making it easy to:a) Access shared data from anywhere
b) Avoid prop drilling (passing props through many layers)
ii) Predictability of State
React:
State can be modified in multiple places, leading to unpredictable behavior in large apps.Redux:
State changes only via pure functions (reducers) and explicit actions, making changes:a) Predictable
b) Easier to debug and trace
iii) Better Debugging ToolsReact:
Limited to browser and component-level inspection.Redux:
Integrates with Redux DevTools, offering,a) Time-travel debugging
b) State change history
c) Action logging
iv) Middleware SupportReact:
Custom logic like logging, async calls, and analytics are manually handled.Redux:
Middleware (e.g., Redux Thunk, Redux Saga) allows:a) Handling side effects like async API calls
b) Logging
c) Error reporting
v) ScalabilityReact:
As the app grows, managing and sharing state across components becomes difficult.Redux:
With its organized structure (actions → reducers → store), Redux:a) Scales better in large apps
b) Makes it easier to maintain and test logic
vi) Easier TestingReact:
Testing component logic can be intertwined with state.Redux:
Reducers and actions are pure functions, making them easy to:a) Unit test
b) Mock state transitions
vii) Consistent Data FlowReact:
State updates might trigger re-renders unpredictably across component trees.Redux:
Enforces a strict unidirectional data flow:Action → Reducer → New State → UI update
When to Use Redux?Use Redux if:
a) Your app has complex state logic
b) You need to share state across many components
c) You want time-travel debugging
d) You're building enterprise-level or large-scale apps
For small apps or local state, React's built-in state + Context may be enough.
What is the purpose of the constants in Redux?
In Redux, constants are typically used to define action types. These are string values that describe what kind of action is being performed (e.g.,"ADD_TODO"or"FETCH_USER_SUCCESS").
i) Avoid TyposUsing constants helps avoid mistakes like this,
dispatch({ type: ADD_TODO }); // typo would cause a reference error insteadii) Code Reusability and Centralization
Action types are declared in one file (e.g.,
actionTypes.js) and used throughout the app,// actionTypes.js export const ADD_TODO = 'ADD_TODO'; export const DELETE_TODO = 'DELETE_TODO';Now both actions and reducers import from the same source, reducing duplication and increasing consistency.
iii) MaintainabilityKeeping all action types in one place makes your codebase easier to,
a) Audit
b) Refactor
c) Understand
Especially useful in large-scale applications.
iv) Collaboration and ScalabilityIn large teams, having constants helps developers:
a) Understand the available actions
b) Reuse existing ones instead of creating duplicates
v) Integration with ToolsRedux dev tools, logs, and testing frameworks can benefit from having standard action type strings to hook into or assert against.
Constants (actionTypes.js)export const INCREMENT = 'INCREMENT'; export const DECREMENT = 'DECREMENT';Action Creator (actions.js)
import { INCREMENT } from './actionTypes'; export const increment = () => ({ type: INCREMENT });Reducer (counterReducer.js)
import { INCREMENT, DECREMENT } from './actionTypes'; const counterReducer = (state = 0, action) => { switch (action.type) { case INCREMENT: return state + 1; case DECREMENT: return state - 1; default: return state; } };What is a store in Redux?
In Redux, the store is the central and single source of truth for your application's state. It is an object that holds the entire state tree of your app and provides methods to access and update that state in a predictable way.
Key Responsibilities of the Redux Storei) Holds the application state
ii) Allows access to state via
getState()iii) Allows state to be updated via
dispatch(action)iv) Registers listeners via
subscribe(listener)v) Handles state changes through reducers
import { createStore } from 'redux'; import rootReducer from './reducers'; const store = createStore(rootReducer);***Common Store Methods
***i) getState() - Returns the current state
ii) dispatch() - Sends an action to trigger a state change
iii) subscribe() - Registers a callback that runs when the state changes
iv) replaceReducer() - Replaces the reducer (used mostly for hot-reloading or dynamic modules)How Store Works in Redux Flow
i) A component dispatches an actionii) The store sends the action to the reducer
iii) The reducer returns a new state
iv) The store updates its internal state
v) All subscribed components are notified and can re-render
const store = createStore(counterReducer); store.subscribe(() => { console.log('Current State:', store.getState()); }); store.dispatch({ type: 'INCREMENT' }); // Output: Current State: 1The Redux store is essential for managing and synchronizing state in larger or more complex React applications.
What is an action in Redux?
In Redux, an action is a plain JavaScript object that describes an event or change you want to make to the application state.
Think of an action as a messenger that carries instructions from your app to the Redux store.
Structure of an Action
An action must have atypeproperty (a string), and it can have additional data (payload),{ type: 'ADD_TODO', payload: { text: 'Learn Redux' } }Action Properties
i)
type: A required string constant that tells Redux what kind of action is happeningii)
payload: Optional data that helps reducers update the state// Action to add a to-do const addTodo = { type: 'ADD_TODO', payload: { text: 'Buy milk' } }; // Action to toggle a task const toggleTodo = { type: 'TOGGLE_TODO', payload: { id: 1 } };Dispatching an Action
Actions are sent to the Redux store using thedispatch()methodstore.dispatch(addTodo);How Actions Fit into Redux Flow
i) A user interaction or event triggers an action
ii) The action is dispatched to the store
iii) The store passes the action to reducers
iv) Reducers process the action and return new state
v) The UI updates based on the new state
Example With Action Creator Function
Instead of hardcoding the action, you often use action creators,const addTodo = (text) => { return { type: 'ADD_TODO', payload: { text } }; }; store.dispatch(addTodo('Learn Redux'));Actions are the only way to tell Redux what to do — making your app’s behavior predictable and traceable.
What is a reducer in Redux?
In Redux, a reducer is a pure function that determines how the application state changes in response to an action.
A reducer takes the current state and an action, and returns a new state — without mutating the original state.
Reducer Signature(state, action) => newStateKey Characteristics of Reducers
i) Pure function - Always returns the same output for the same inputs, no side effects
ii) Immutable - Does not modify the existing state, returns a new state object
iii) Switch-case - Commonly uses a switch on action.type to determine how to update stateconst counterReducer = (state = 0, action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } }; // state = 0 is the initial/default state. // When INCREMENT or DECREMENT is dispatched, it returns the updated state. // For any unknown action, it returns the current state unchanged.Example: Todo List Reducer
const todosReducer = (state = [], action) => { switch (action.type) { case 'ADD_TODO': return [...state, { id: action.id, text: action.text, completed: false }]; case 'TOGGLE_TODO': return state.map(todo => todo.id === action.id ? { ...todo, completed: !todo.completed } : todo ); default: return state; } };***Combining Reducers
***Redux allows you to combine multiple reducers usingcombineReducers()import { combineReducers } from 'redux'; const rootReducer = combineReducers({ counter: counterReducer, todos: todosReducer });Reducers are the heart of Redux, defining how every action transforms your app’s state.
Explain the concept of a Middleware in Redux.
In Redux, middleware is a function that extends the store’s capabilities. It sits between the dispatching of an action and the moment it reaches the reducer.
Middleware allows you to intercept, log, delay, or modify actions before they reach the reducer.
Why Use Middleware?
Middleware is useful for handling,i) Asynchronous operations (e.g., API calls)
ii) Logging and debugging
iii) Conditionally dispatching actions
iv) Error handling
v) Analytics tracking
Redux Flow with MiddlewareComponent → dispatch(action) ↓ [Middleware] ↓ Reducer → New StateLogging Middleware
const logger = store => next => action => { console.log('Dispatching:', action); const result = next(action); // pass action to next middleware or reducer console.log('Next State:', store.getState()); return result; }; // store: Redux store instance // next: function that passes the action to the next middleware or reducer // action: the action being dispatchedApplying Middleware
Use Redux’sapplyMiddleware()when creating the storeimport { createStore, applyMiddleware } from 'redux'; const store = createStore( rootReducer, applyMiddleware(logger) );Common Middleware Libraries
i) redux-thunk - Handles async logic like API calls
ii) redux-saga - More powerful async middleware using generators
iii) redux-logger - Logs actions and state to console
iv) redux-promise - Dispatches promises as actions
Middleware makes Redux powerful and flexible, enabling complex logic while keeping reducers pure and focused.What are the Ways To Access the Redux Store?
In a Redux-powered React app, the Redux store holds the global state, and there are several ways to access or interact with it.
i) UsinguseSelectorHook (Modern & Recommended)
A React-Redux hook that lets you read data from the store inside functional components.import { useSelector } from 'react-redux'; const count = useSelector(state => state.counter);a) Easy to use in function components
b) Subscribes to store updates automatically
ii) UsinguseDispatchHook (Modern)
Allows you to dispatch actions to the Redux store from a functional component.import { useDispatch } from 'react-redux'; const dispatch = useDispatch(); dispatch({ type: 'INCREMENT' });iii) Using
connect()Function (Legacy/Classic)
A higher-order component (HOC) fromreact-reduxto connect class components (or functional ones) to the store.import { connect } from 'react-redux'; const MyComponent = ({ count, increment }) => ( <div> <p>{count}</p> <button onClick={increment}>+</button> </div> ); const mapStateToProps = state => ({ count: state.counter }); const mapDispatchToProps = dispatch => ({ increment: () => dispatch({ type: 'INCREMENT' }) }); export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);a) Well-suited for class components
b) Still widely used in legacy code bases
iv) Usingstore.getState()Directly (Outside React)
You can directly callgetState()on the store object, typically outside React components (e.g., in middleware or setup scripts).const state = store.getState(); console.log(state);v) Using
Providerto Make Store Accessible
Before accessing the Redux store in React, you must wrap your app with<Provider>so the store is available to the whole component tree.import { Provider } from 'react-redux'; import store from './store'; <Provider store={store}> <App /> </Provider>In modern React (v16.8+),
useSelectoranduseDispatchare the most concise and efficient ways to access the Redux store in function components.What is the difference between mapStateToProps() and mapDispatchToProps()?
In a React-Redux application,mapStateToProps()andmapDispatchToProps()are two key functions used with theconnect()function to link your React components to the Redux store.
i) Purpose
mapStateToProps() - Maps state from Redux store to component props
mapDispatchToProps() - Maps dispatchable actions to component props
ii) Input
mapStateToProps() - Takes the entire Redux state as an argument
mapDispatchToProps() - Takes thedispatchfunction as an argument
iii) Output
mapStateToProps() - Returns an object of state data
mapDispatchToProps() - Returns an object of functions that dispatch actions
iv) Used For
mapStateToProps() - Reading data from the store
mapDispatchToProps() - Sending data/commands to the store
v) Usage
mapStateToProps() - Component can access state via props
mapDispatchToProps() - Component can trigger actions via props
Redux State -const initialState = { count: 0 };ii) Reducer -
function counterReducer(state = initialState, action) { switch (action.type) { case 'INCREMENT': return { count: state.count + 1 }; default: return state; } }iii) Component with
connectimport React from 'react'; import { connect } from 'react-redux'; const Counter = ({ count, increment }) => ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); // 🔹 mapStateToProps: map Redux state to props const mapStateToProps = state => ({ count: state.count }); // 🔹 mapDispatchToProps: map dispatch to props const mapDispatchToProps = dispatch => ({ increment: () => dispatch({ type: 'INCREMENT' }) }); export default connect(mapStateToProps, mapDispatchToProps)(Counter);i) mapStateToProps - Pulls data from the Redux store
ii) mapDispatchToProps - Sends actions to the Redux storeHow is Relay Different From Redux?
Relay and Redux are both state management tools, but they serve very different purposes and are often used in different contexts.
Relay is a GraphQL client focused on optimizing how data is fetched and updated.Redux is a state management library that gives you manual control over how state flows through your app.
Use Relay when working with GraphQL APIs.
Use Redux for managing complex local or global app state regardless of backend.
i) Purpose
Relay - Data-fetching layer for GraphQL
Redux - Predictable state management
ii) Data Source
Relay - Fetches data from a GraphQL server
Redux - Manages client-side state
iii) Architecture
Relay - Built around GraphQL and fragments
Redux - Built around actions, reducers, and store
iv) Updates
Relay - Handles data normalization and caching automatically
Redux - Requires manual updates through reducers
v) Setup Complexity
Relay - Higher (tight integration with GraphQL)
Redux - Lower (generic and flexible)
vi) Reactivity
Relay - Automatic with GraphQL and subscriptions
Redux - Manual, unless using tools like Redux-Saga
vii) Use Case
Relay - Best for apps with GraphQL APIs
Redux - Best for apps needing centralized local state
viii) Data Fetching
Relay - Built-in and declarative (useFragment, etc.)
Redux - Needs middleware likeredux-thunkorredux-sagafor async
ix) Learning Curve
Relay - Steeper (GraphQL + Relay concepts)
Redux - Moderate (but more intuitive than Relay)How to structure Redux top level directories?
Structuring your Redux directories in a scalable and maintainable way is crucial, especially as your application grows. Below is a recommended directory structure for organizing your Redux code, along with explanations and variations based on complexity.src/ ├── app/ │ ├── store.js // Redux store configuration │ └── rootReducer.js // Combines all reducers │ ├── features/ // Feature-based folders │ ├── auth/ │ │ ├── authSlice.js // Contains reducer, actions, selectors │ │ ├── authAPI.js // API calls related to auth │ │ └── authSelectors.js │ │ │ ├── todos/ │ │ ├── todosSlice.js │ │ ├── todosAPI.js │ │ └── todosSelectors.js │ ├── components/ // UI components │ ├── pages/ // Route-based pages │ └── index.js // Entry pointi)
app/Central Redux config files
Contains
store.js,rootReducer.js, and optionallymiddleware.js
ii)features/Follows the “ducks” pattern or feature-based grouping
Each feature (e.g.,
auth,todos) contains its own slice
iii)components/Reusable UI components
Connected or presentational components can live here
iv)pages/Route-based components (e.g.,
/login,/dashboard)
Domain-Driven (Group by feature/domain)features/ ├── user/ ├── product/ ├── cart/Classic Redux
redux/ ├── actions/ ├── reducers/ ├── types/What is the difference between React context and React Redux?
Both React Context and React Redux are used to share state across components in a React application, but they serve different purposes and excel in different scenarios.
i) Purpose
React Context - Built-in way to share data between components (e.g. theme, locale)
React Redux - External library for managing global app state
ii) Data Type
React Context - Ideal for static or rarely changing data
React Redux - Designed for dynamic, complex, and large-scale state
iii) Performance
React Context - Less efficient on frequent updates (can cause re-renders)
React Redux - Highly optimized for performance with selective re-renders
iv) Re-render Control
React Context - Limited (value change re-renders all consumers)
React Redux - Fine-grained control usingconnect,useSelector, memoization
v) Data Flow
React Context - One-way data flow
React Redux - Unidirectional data flow via actions and reducers
vi) Boilerplate
React Context - Minimal setup
React Redux - Requires setup: store, reducers, actions, etc.
vii) Middleware Support
React Context - No built-in middleware support
React Redux - Supports middleware likeredux-thunk,redux-saga
viii) Dev Tools
React Context - No built-in dev tools
React Redux - Comes with powerful Redux DevTools
ix) Learning Curve
React Context - Simple and built into React
React Redux - Steeper curve (Redux concepts + configuration)
x) Use Case
React Context - Theming, auth status, user preferences
React Redux - Complex state (e.g. UI + API data, pagination, caching)
When to Use What?
i) Light, static global state (e.g. theme, language) - React Context
ii) Complex state with many interrelated parts - Redux
iii) Frequent state updates affecting large component trees - Redux
iv) Sharing config or environment variables - React Conetxt
React Context// ThemeContext.js const ThemeContext = React.createContext('light'); const App = () => ( <ThemeContext.Provider value="dark"> <SomeComponent /> </ThemeContext.Provider> );Redux
// Redux example dispatch({ type: "ADD_TODO", payload: "Learn Redux" }); const todos = useSelector(state => state.todos);What is redux-saga?
Redux-Saga is a middleware library for handling side effects (like asynchronous operations) in a Redux application using ES6 generators.It allows you to write more manageable, testable, and powerful asynchronous flows, such as:
Data fetching, Delays, Accessing browser APIs, Listening for actions and dispatching other actions.
Why Use Redux-Saga?Redux by itself handles synchronous state changes well. But side effects like,
i) Fetching data from an API
ii) Delaying actions
iii) Retrying failed requests
are complex to manage using just
redux-thunk. Redux-Saga offers a cleaner, more scalable alternative using generator functions.
Saga Functionimport { call, put, takeEvery } from 'redux-saga/effects'; import axios from 'axios'; // Worker Saga function* fetchUser(action) { try { const user = yield call(axios.get, `/api/user/${action.payload}`); yield put({ type: 'USER_FETCH_SUCCEEDED', user: user.data }); } catch (e) { yield put({ type: 'USER_FETCH_FAILED', message: e.message }); } } // Watcher Saga function* mySaga() { yield takeEvery('USER_FETCH_REQUESTED', fetchUser); }Add to Middleware
import createSagaMiddleware from 'redux-saga'; import { configureStore } from '@reduxjs/toolkit'; import mySaga from './sagas'; const sagaMiddleware = createSagaMiddleware(); const store = configureStore({ reducer: rootReducer, middleware: [sagaMiddleware] }); sagaMiddleware.run(mySaga);Benefits of Redux-Saga
i) Clean, declarative syntax
ii) Powerful control over flow (cancel, debounce, throttle, retry)
iii) Easier to test due to pure functions
iv) Handles complex async logic better than thunks
Use When,i) You have complex async flows
ii) Need advanced side-effect control (like race conditions, retries, debouncing)
iii) Want testable async logic
How to set initial state in Redux?
Setting the initial state in Redux is straightforward and essential—it defines the starting values of your application’s state before any actions are dispatched.
i) Set Initial State Directly in the Reducer// counterReducer.js const initialState = { count: 0 }; function counterReducer(state = initialState, action) { switch (action.type) { case 'INCREMENT': return { ...state, count: state.count + 1 }; default: return state; } } export default counterReducer;Here,
state = initialStateensures that if the state isundefined(as it is on first run), Redux usesinitialState.
ii) Pass Initial State to the Store (optional override)
You can also pass an initial state directly tocreateStore(for legacy Redux) orconfigureStorefrom Redux Toolkit, though this is more common in testing or SSR.import { createStore } from 'redux'; import rootReducer from './reducers'; const preloadedState = { counter: { count: 10 } }; const store = createStore(rootReducer, preloadedState);Best Practices
i) Define initial state within each reducer or slice for modularity.
ii) Use
preloadedStateif:You need to hydrate state from localStorage or server.
You're writing unit tests and want to inject custom state.
What are the differences between call() and put() in redux-saga?
In redux-saga,call()andput()are both effects, used to perform side effects in a more declarative and testable way. However, they serve very different purposes.
i) call() – For Calling Functions (like APIs)
To invoke a function or asynchronous operation, such as an API call, with the option to pause and wait for its result.import axios from 'axios'; function* fetchUser(id) { const user = yield call(axios.get, `/api/user/${id}`); // waits until axios.get resolves }ii) put() – For Dispatching Redux Actions
To dispatch an action to the Redux store (similar todispatch()in regular Redux).function* fetchUser(id) { try { const response = yield call(axios.get, `/api/user/${id}`); yield put({ type: 'USER_FETCH_SUCCEEDED', payload: response.data }); } catch (e) { yield put({ type: 'USER_FETCH_FAILED', error: e.message }); } }Example
function* fetchUserSaga(action) { try { // 1. Call API const user = yield call(fetchUserApi, action.payload); // 2. Dispatch success action yield put({ type: 'USER_FETCH_SUCCESS', payload: user }); } catch (error) { // 3. Dispatch error action yield put({ type: 'USER_FETCH_FAILURE', error: error.message }); } }call() - "Wait for a function to finish."
put() - "Dispatch an action to Redux."What are Redux selectors and why use them?
Redux selectors are pure functions used to extract specific pieces of state from the Redux store.Instead of accessing state directly in your components (
state.someSlice.someValue), you use a selector like,const someValue = useSelector(selectSomeValue);Why Use Selectors?
i) Abstraction - Encapsulates how state is accessed, so components don’t depend on the exact structure of the state tree.
ii) Reusability - One selector function can be reused across multiple components.
iii) Performance - Memoized selectors (usingreselect) avoid unnecessary recalculations and re-renders.
iv) Testability - Pure functions that are easy to unit test.
v) Maintainability - Easier to change the state shape later, without updating all component logic.
Define a selector function// features/user/userSelectors.js export const selectUser = (state) => state.user; export const selectUserName = (state) => state.user.name;Use it in a component
import { useSelector } from 'react-redux'; import { selectUserName } from './features/user/userSelectors'; const UserProfile = () => { const name = useSelector(selectUserName); return <h1>Hello, {name}</h1>; };Where to Put Selectors?
Keep selectors in the same directory as the corresponding slice or in aselectors.jsfile per feature,features/ ├── todos/ │ ├── todosSlice.js │ ├── todosAPI.js │ └── todosSelectors.jsWhat is Concurrent Rendering in React?
Concurrent Rendering is a React feature that enables the React rendering engine to interrupt, pause, resume, and reuse work on multiple components — improving responsiveness and user experience.It allows React to work on multiple tasks simultaneously without blocking the main thread, making UIs more responsive, especially during complex or heavy updates.
Without concurrent rendering, React follows a synchronous, blocking model, It renders the entire component tree from top to bottom — if it’s slow, your UI freezes.
With Concurrent Rendering, React can pause mid-render, let urgent tasks (like a button click) through, and resume work later — making apps feel faster and more responsive.
i) Concurrent Rendering is opt-in via certain APIs — you don’t need to "enable" it globally.ii) It does not mean multithreading — React still runs on the main thread.
iii) It’s about scheduling and interruptibility, not parallel execution.
***Benefits of Concurrent Rendering
***i) Interruptible rendering - Pause rendering work to do more important updates first
ii) Improved responsiveness - Keeps the app usable even during expensive updates
iii) Prioritized updates - Differentiates between urgent and non-urgent tasks
iv) Better user experience - Smooth transitions, less freezing, better perceived performance
Real-World Use Casesi) Search filters on large lists
ii) Infinite scrolling
iii) Animations during state updates
iv) Lazy loading content with fallback
What is forwardRef function in React?
TheforwardReffunction in React is used to pass a ref through a component to one of its child components, typically a DOM element.By default, refs do not get passed down from a parent to a child component, but
forwardRefenables this behavior explicitly.
Only function components can be used withforwardRef(not regular arrow functions).You cannot use hooks inside the
forwardRefcall itself—treat it like a regular functional component.
Why Use forwardRef?i) You’re building reusable components (like inputs, buttons) and want the parent to access the DOM node inside.
ii) You need to control focus, measure size, or trigger animations from a parent component.
const MyComponent = React.forwardRef((props, ref) => { return <input ref={ref} {...props} />; }); // ref: Comes from the parent component // props: The usual props // forwardRef: Wraps the component and gives access to the refDefine a Component Using
forwardRefimport React, { forwardRef } from 'react'; const FancyInput = forwardRef((props, ref) => { return <input ref={ref} className="fancy" {...props} />; });Use It in a Parent
import React, { useRef } from 'react'; function ParentComponent() { const inputRef = useRef(); const focusInput = () => { inputRef.current.focus(); // access the DOM node }; return ( <> <FancyInput ref={inputRef} /> <button onClick={focusInput}>Focus Input</button> </> ); }Common Use Cases
i) Custom input components
ii) UI libraries (e.g. Material UI, Chakra UI)
iii) Handling animations with libraries like Framer Motion
iv) Integrating with non-React libraries that expect DOM access
What is React hydration?
React Hydration is the process by which React attaches event listeners and reuses the existing HTML markup generated by the server during Server-Side Rendering (SSR).Hydration ensures that the React app becomes interactive on the client without re-rendering the entire DOM, which helps improve performance and perceived load time.
How It Worksi) Server renders the initial HTML (SSR).
ii) The HTML is sent to the browser.
iii) React "hydrates" the HTML - it Attaches event handlers, Initializes React's internal state, Avoids replacing existing DOM nodes (reuses them)
// On the client import { hydrateRoot } from 'react-dom/client'; hydrateRoot(document.getElementById('root'), <App />);Benefits of Hydration
i) Fast first paint - Browser can show static HTML before React takes over
ii) Improved performance - Avoids re-generating the DOM — reuses what SSR created
iii) Better SEO - Search engines see fully rendered HTML
iv) Full interactivity - Event listeners are added once React hydrates the appConsiderations
i) Mismatch Warnings: If server and client HTML differ, React will warn or replace parts of the DOM.
ii) Hydration errors can happen due to, Using
window,document, or other browser-only APIs during SSR. Non-deterministic rendering (e.g., random values or time-based data on first render).iii) Use
useEffect()(notuseLayoutEffect()) for client-only logic to avoid SSR warnings.What is the difference between try catch block and error boundaries in React?
In React,try...catchblocks and Error Boundaries are both mechanisms for handling errors, but they are used in different contexts and have different limitations and capabilities.
i) try...catch Block
Use Casea) Used to catch synchronous errors in JavaScript code.
b) Typically used in event handlers, functions, or any imperative logic.
Characteristicsa) Works only for imperative code (i.e., code you call directly).
b) Cannot catch errors during rendering, in lifecycle methods, or in asynchronous code unless awaited properly.
try { const data = JSON.parse('invalid json'); } catch (error) { console.error('Caught an error:', error); }ii) Error Boundaries
Use Casei) Used to catch rendering errors in React component trees.
ii) Designed for React components, especially in the UI rendering lifecycle.
Characteristicsi) Catch errors during rendering, in lifecycle methods, and in constructors of child components.
ii) Do not catch:
Errors in event handlers (you must use
try...catchthere).Errors in asynchronous code like
setTimeout, Promises, orfetch(unless you handle those in.catch()).class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, errorInfo) { console.error('Error caught by Error Boundary:', error, errorInfo); } render() { if (this.state.hasError) { return <h2>Something went wrong.</h2>; } return this.props.children; } } // Usage <ErrorBoundary> <MyComponent /> </ErrorBoundary>When to Use What?
i) Usetry...catchfor general JS errors in business logic, async code, or event handlers.ii) Use Error Boundaries to protect your React UI from crashing due to bugs in component rendering.
Explain CORS in React?
CORS (Cross-Origin Resource Sharing) is not specific to React, but it often becomes an issue when building React apps that interact with a backend API.
What is CORS
CORS is a security feature implemented by browsers to restrict cross-origin HTTP requests initiated from scripts running in the browser.
CORS is enforced by the browser, not by React.
The backend server must include the appropriate CORS headers in its response to allow the React frontend to access it.// Example of Cors headers Access-Control-Allow-Origin: http://localhost:3000 Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Typei) Origin = protocol + domain + port
ii) A request is cross-origin when your React app (usually running on
localhost:3000) tries to make a request to an API on a different origin, likehttp://api.example.comor evenlocalhost:5000.
Common Scenario
You run a React app locallyhttp://localhost:3000And try to fetch data from a backend API
fetch('http://localhost:5000/api/data')The browser blocks this request with a CORS error unless the server at
localhost:5000explicitly allows it.
How to Fix CORS in Development
i) Enable CORS on the Server// In Node.js/Express (backend) const cors = require('cors'); app.use(cors({ origin: 'http://localhost:3000' }));ii) Use a Proxy in React (for development only)
// Set up a proxy in package.json "proxy": "http://localhost:5000" // This tells the development server to proxy API requests to the backend, // avoiding CORS issues. fetch('/api/data') // This gets proxied to http://localhost:5000/api/dataHow can we provide the security for any React Application?
Securing a React application involves both frontend precautions and strong backend security. React is just the UI layer, so the real security enforcement happens on the server, but you can—and should—harden your React app to avoid common risks.
How to Secure a React Application
i) Protect Against XSS (Cross-Site Scripting)// React is safe by default — it escapes values in JSX <p>{userInput}</p> // Safe: React escapes it // Avoid dangerouslySetInnerHTML unless absolutely necessary <div dangerouslySetInnerHTML={{ __html: untrustedHTML }} /> // ⚠️ Risky // Sanitize data if you must use raw HTML (use libraries like DOMPurify)ii) Use HTTPS
Always serve your app over HTTPS, especially in production.Enforce HTTPS using server configurations or redirects.
iii) Handle Authentication SecurelyUse JWT (JSON Web Tokens), OAuth, or session-based auth.
Store tokens securely:
Best Practice: Use HTTP-only cookies (more secure than localStorage).
Avoid storing JWTs in
localStorageorsessionStorageif you can (they're vulnerable to XSS).
iv) Implement Role-Based Access Control (RBAC)
On the frontend, hide UI elements based on user roles.On the backend, enforce permissions — never trust the frontend to control access.
{user.role === 'admin' && <AdminPanel />}v) Avoid Exposing Sensitive Data in the Frontend
Never include secrets, API keys, or passwords in your React code.
Even
envvariables in.envfiles (likeREACT_APP_SECRET) are exposed in the client bundle!
vi) Secure Your APIUse CORS to restrict who can access your backend.
Validate and sanitize all inputs server-side.
Rate-limit and throttle requests to prevent abuse (e.g., with libraries like
express-rate-limit).
vii) Use Dependency Audits and UpdatesKeep dependencies up to date.
Use tools like:
npm audityarn auditGitHub Dependabot
viii) Enable Code Splitting and Lazy LoadingReduces attack surface by loading only what’s needed.
Use React's
React.lazy()andSuspenseto defer loading code.
ix) Security Headers (via Backend)
If serving React from Node/Expressnpm install helmetconst helmet = require('helmet'); app.use(helmet());How can we improve the performance of the React Application?
Improving the performance of a React application is essential for ensuring fast load times, smooth interactions, and a better user experience. Here’s a comprehensive guide to performance optimization in React,
i) Use React’s Production Build
Use the optimized production version by running,npm run buildthis minifies code
it removes development warnings
it also enables performance optimizations
ii) Code Splitting withReact.lazy()andSuspenseSplit code and load components only when needed
const AdminPanel = React.lazy(() => import('./AdminPanel')); <Suspense fallback={<Spinner />}> <AdminPanel /> </Suspense>Reduces initial bundle size and improves load speed.
iii) Memoization Techniques
React.memo() - Prevent re-rendering of functional components unless props changeconst MyComponent = React.memo(({ data }) => { ... });useMemo()anduseCallback()-useMemocaches expensive calculationsuseCallbackcaches functions to avoid unnecessary rendersconst filteredList = useMemo(() => filterData(data), [data]); const handleClick = useCallback(() => doSomething(id), [id]);iv) Avoid Unnecessary Re-renders
Structure components well and avoid prop drilling.Use tools like React DevTools to identify re-renders.
Lift state up only when necessary.
v) Virtualize Long Lists
Use libraries likereact-windoworreact-virtualizedimport { FixedSizeList as List } from 'react-window'; <List height={500} itemCount={1000} itemSize={35}> {({ index, style }) => <div style={style}>Item {index}</div>} </List>Renders only visible items for large lists — massive performance gain.
vi) Debounce Input Handlers
For expensive actions like API calls on input change,const debouncedSearch = useMemo(() => debounce(searchFunc, 300), []);Use
lodash.debounceor a custom debounce function.
vii) Use Efficient Images and Assets
Compress and optimize images.Use
WebPorAVIFformats.Use
lazy loadingfor images<img src="image.jpg" loading="lazy" alt="example" />How do you handle performance optimization in a React application?
Handling performance optimization in a React application means making the UI faster, more responsive, and efficient by reducing unnecessary work and improving the way your app loads and updates components. Here's how I would handle it systematically,
i) Build with Production Optimizations
I always ensure the app is built withnpm run buildfor production. This: Minifies code, Removes development warnings, Optimizes React for performance
ii) Code Splitting & Lazy LoadingI use
React.lazy()andSuspenseto split code and defer loading components until they are neededconst Dashboard = React.lazy(() => import('./Dashboard'));Reduces initial load time by loading parts of the app on demand.
iii) Prevent Unnecessary Re-rendersReact.memo()for pure componentsuseCallback()for functions passed as propsuseMemo()for expensive calculationsconst filteredData = useMemo(() => expensiveFilter(data), [data]);Keeps components from re-rendering unless they really need to.
iv) Optimize State Management
I avoid "lifting state up" unnecessarily and scope state locally when possible. For global state, I:Prefer
useReducerfor complex state logicUse optimized state libraries like Zustand or Jotai over bulky ones when possible
v) Virtualize Large Lists
When dealing with large datasets, I usereact-windoworreact-virtualized<FixedSizeList itemCount={1000} itemSize={35} height={500} width={300}> {({ index, style }) => <div style={style}>Row {index}</div>} </FixedSizeList>Prevents DOM bloat by only rendering visible items.
vi) Debounce Expensive Input HandlingTo avoid laggy UIs during typing/searching, I debounce handlers
const debouncedSearch = useMemo(() => debounce(handleSearch, 300), []);Smoothens interactions and avoids unnecessary re-renders or API calls.
vii) Optimize Images & AssetsUse compressed images in
WebPformat.Lazy-load images with the
loading="lazy"attribute.Use SVGs for simple icons/logos.
viii) Minimize Bundle SizeI analyze the bundle with
source-map-explorerorwebpack-bundle-analyzer.Use only needed parts of libraries (e.g.,
import debounce from 'lodash/debounce').Avoid heavy libraries when a smaller utility will do.
ix) Use Browser Caching and Service WorkersFor progressive apps,
I set up service workers with tools like
Workbox.Cache static assets, fonts, and API responses.
x) Monitor and Audit PerformanceI regularly use,
React DevTools to inspect component renders.
Chrome Lighthouse for performance scores.
Web Vitals to measure real user metrics.
How to check the performance of React app using LightHouse?
To check the performance of a React app using Lighthouse, you can use Google Chrome DevTools, which includes Lighthouse as a built-in feature.
i) Open Your React App in Chrome
Make sure it's running either locally (e.g.,http://localhost:3000) or deployed online.
ii) Open Chrome DevToolsRight-click anywhere on the page → click Inspect, or
Press
Ctrl + Shift + I(Windows/Linux) orCmd + Option + I(Mac)
iii) Go to the "Lighthouse" TabIn DevTools, click the "Lighthouse" tab.
If you don’t see it, click the
»icon to find it in the overflow menu.
iv) Select Audit Categories
Usually, for React apps, you’ll want at least:Performance, Best Practices, Accessibility
v) Choose Device TypeChoose Mobile (default) or Desktop
Mobile emulation simulates a slower device/network — good for real-world performance insights.
vi) Run the AuditClick "Analyze page load" or "Generate report".
Lighthouse will reload your page, run a series of tests, and then show a detailed report.
How to enable production mode in React?
To enable production mode in React, you need to build your app using the production configuration provided by your build tool (e.g., Create React App, Vite, Webpack, etc.).
i) Using Create React App (CRA)npm run build # or yarn buildThis does the following:
Sets
process.env.NODE_ENVto'production'Minifies and optimizes your code
Removes React development warnings and logs
The production-ready files will be output to the
build/directory.
To serve the production build locally,npm install -g serve serve -s buildii) Using Vite
Run the production build command,npm run buildThis creates an optimized build in the
dist/folder.To preview it
npm run previewiii) Using Webpack (Custom Setup)
In your Webpack config,mode: 'production'Then run your custom production build,
webpack --mode productionWhy Production Mode Matters
i) Warnings & Debug Info
Development Mode - Enabled
Production Mode - Disabled
ii) Performance
Development Mode - Slower
Production Mode - Optimized
iii) Bundle Size
Development Mode - Larger
Production Mode - Minified & Tree-shaken
iv) Environment Variable
Development Mode - NODE_ENV=development
Production Mode - NODE_ENV=productionWhar are the efficient way to deply your React Application?
Deploying a React application efficiently depends on your project’s complexity, scalability requirements, and your team's familiarity with deployment tools. Here are some efficient and popular ways to deploy a React app,
i) Vercel (Recommended for Simplicity and Performance)
Prosa) Automatic deployments from GitHub/GitLab/Bitbucket
b) Built-in CDN for fast loading
c) Serverless functions supported
d) Great DX (developer experience)
Usage
a) Push code to a Git repository
b) Connect the repo to Vercel
c) Vercel auto-builds and deploys on push
ii) NetlifyPros
a) Similar to Vercel with automatic Git-based deployments
b) Supports serverless functions and forms
c) Custom domain and HTTPS setup with ease
Usage
a) Connect your repo
b) Configure build command (
npm run build)c) Set publish directory to
build/
iii) GitHub PagesPros
a) Free and simple for static sites
Cons
a) Only works for static builds
Usage
a) Build the app:
npm run buildb) Use the
gh-pagespackage to push thebuild/folderc) Add
"homepage": "https://<username>.github.io/<repo>"topackage.json
iv) Firebase HostingPros
a) Fast CDN, custom domains, and HTTPS
b) Good for apps that may use Firebase backend services
Usage
a) Install Firebase CLI:
npm install -g firebase-toolsb) Initialize:
firebase initc) Deploy:
firebase deploy
v) AWS AmplifyPros
a) Powerful for full-stack apps
b) Git-based CI/CD, serverless backend
Cons
a) Slightly steeper learning curve
Usage
a) Connect Git repository
b) Amplify handles build and deployment
c) Optional: Use CLI for advanced customization
vi) Docker + NGINX (Advanced/Production)Pros
a) Fully customizable and secure
b) Great for enterprise or containerized environments
Cons
a) Requires infrastructure knowledge
Usage
a) Dockerize your React app with NGINX to serve the static files
b) Deploy to any cloud provider (AWS EC2, DigitalOcean, etc.)
Tips for Efficient Deploymenti) Use a CDN to cache and serve static assets faster
ii) Enable gzip or Brotli compression for minimized file sizes
iii) Lazy load components to reduce initial bundle size
iv) Monitor performance using tools like Lighthouse or Web Vitals
v) Automate deployment using Git hooks or CI/CD (GitHub Actions, GitLab CI)
What is the latest stable version of React and What is new in it?
As of May 2025, the latest stable version of React is React 19.1.0, released on March 28, 2025. This version builds upon the major release of React 19.0.0, which became stable on December 5, 2024.
React 19 introduces several significant features and improvements aimed at enhancing developer experience, performance, and code maintainability.
i) React CompilerA new compiler that automatically optimizes your code, eliminating the need for manual memoization with
useMemo,useCallback, ormemo. This leads to cleaner code and improved performance.
ii) Server ComponentsEnables rendering parts of your UI on the server, reducing client-side JavaScript and improving performance and SEO.
iii) New HooksuseActionState: Manages state transitions in asynchronous actions.useFormStatus: Provides status information for form submissions.useOptimistic: Allows optimistic UI updates, enhancing user experience during async operations.
iv) use() APIA new hook that simplifies data fetching by allowing you to await promises directly within components, reducing boilerplate code.
v) Actions APISimplifies asynchronous state updates by integrating async functions directly into React's rendering cycle, reducing the need for complex state management logic.
vi) Enhanced Concurrent RenderingImprovements to React's concurrent rendering capabilities, including better scheduling and automatic batching of state updates, leading to more responsive applications.
vii) Simplified Ref HandlingYou can now pass refs as regular props, making it easier to work with custom components and reducing the need for
forwardRef.
viii) Native Metadata RenderingSupport for rendering document metadata (
<title>,<meta>,<link>) directly within React components, enhancing SEO and simplifying head management.
How to Upgrade
To upgrade to React 19.1.0, run,npm install react@19.1.0 react-dom@19.1.0Before upgrading, it's recommended to update to React 18.3.1 to identify any deprecated APIs and prepare your codebase for the transition.
How to Update ReactJS to the Latest Version?
Updating ReactJS to the latest version involves a few steps to ensure compatibility and stability. Here's a clear, step-by-step guide,
i) Check Current Versionnpm list react npm list react-domii) Review React Release Notes
Visit the official React blog or GitHub releases to,
Understand breaking changes
Find deprecated APIs
Read migration instructions
iii) Update Dependencies
To update to the latest stable version (e.g., React 19.1.0 as of May 2025)
npm install react@latest react-dom@latestOr with Yarn
yarn add react@latest react-dom@latestiv) Update Related Packages
Update any other related packages you might be using
npm install react-scripts@latest # If using CRA npm install @types/react @types/react-dom # If using TypeScriptAlso update testing libraries if needed
npm install @testing-library/react @testing-library/jest-domv) Update browserslist
Modern React may rely on updated browser features:
Inpackage.json, update"browserslist": [ ">0.2%", "not dead", "not op_mini all" ]vi) Check and Fix Breaking Changes
If moving between major versions (e.g., 18 → 19):
Search for deprecated APIs you use (e.g.,
ReactDOM.render→createRoot)Use the React 19 Migration Guide
vii) Test Thoroughlynpm run build npm test npm startHow to handle Security Vulnerabilities in React Application?
Handling security vulnerabilities in a React application requires a mix of best practices, tooling, and awareness of common threats. Here's a comprehensive guide to secure your React app effectively,
i) Prevent Cross-Site Scripting (XSS)
a) React Auto-EscapesReact automatically escapes values in JSX,
<div>{userInput}</div> // Safeb) Avoid dangerouslySetInnerHTML
Only use when absolutely necessary, and sanitize the input
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userContent) }} />ii) Avoid Insecure Dependencies
Audit and Patch - Regularly run following comandsnpm audit fix npm auditiii) Secure API Communication
a) Use HTTPS for all requestsEnforce HTTPS via your backend or proxy
b) Validate and Sanitize Input on the Server
Even if React validates input, backend must never trust client data
c) Use Authentication and Authorization
Implement proper token-based auth (e.g., JWT, OAuth)
Avoid storing sensitive tokens in
localStorage; useHttpOnlycookies if possible
iv) Set Proper Content Security Policy (CSP)
Prevent inline scripts and external attacks,Content-Security-Policy: default-src 'self'; script-src 'self';Use tools like Helmet (for Express apps),
npm install helmetapp.use(require("helmet")());v) Prevent Cross-Site Request Forgery (CSRF)
If using cookies for auth, implement CSRF tokensUse libraries like
csurf(Node/Express)Use SameSite and HttpOnly cookie flags
vi) Avoid Leaking Sensitive Data
Never commit: API keys, Environment files (.env), Secrets in public GitHub repos
vii) Keep React and Dependencies Updateda) Use latest versions of React, React DOM, and other packages
b) Monitor official changelogs for security patches
viii) Limit Exposure of Build Infoa) Avoid exposing: Build metadata, Version numbers, Stack traces
b) Set
NODE_ENV=productionand configure error boundaries.
ix) Use Error Boundaries
Prevent exposing internal logic to usersExplain the Webpack in React and Nodejs
Webpack is a powerful module bundler used in modern JavaScript applications, especially in projects using React and sometimes with Node.js (mainly for frontend assets).
What Is Webpack?Webpack takes all your project files—JavaScript, CSS, images, etc.—and bundles them into one or more optimized files for the browser.
Core Concepts of Webpacki) Entry: Where Webpack starts bundling.
entry: './src/index.js'ii) Output: Where Webpack saves the bundled files.
output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }iii) Loaders: Transform non-JS files (like JSX, CSS, images) into valid modules.
babel-loader(transpile JSX/ES6)css-loader,style-loader(handle CSS)file-loader,url-loader(handle assets)
iv) Plugins: Perform tasks like optimizing bundles, generating HTML, cleaning the output directory.HtmlWebpackPluginCleanWebpackPluginMiniCssExtractPlugin
v) Mode: Eitherdevelopmentorproductionmode: 'development' // or 'production'Webpack in React
i) Why It’s Useda) Bundle JSX and modern JavaScript
b) Enable hot module reloading
c) Optimize performance (tree shaking, code splitting)
// webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.jsx', output: { filename: 'bundle.js', path: __dirname + '/dist', }, module: { rules: [ { test: /\.(js|jsx)$/, use: 'babel-loader', exclude: /node_modules/, }, { test: /\.css$/, use: ['style-loader', 'css-loader'], } ] }, plugins: [ new HtmlWebpackPlugin({ template: './public/index.html' }) ], resolve: { extensions: ['.js', '.jsx'], }, devServer: { static: './dist', hot: true, } };Webpack in Node.js
Webpack isn’t typically used to bundle backend Node.js code (since Node understands modules), but it can be used for:a) Bundling frontend assets in a full-stack app
b) Serverless deployments or SSR setups
c) Using Webpack for Lambda Functions (e.g., with AWS)
Use Case ExampleA Node.js + Express server with Webpack to serve a React frontend:
a) React app → Bundled by Webpack
b) Served by Express from
/dist