Skip to main content

Command Palette

Search for a command to run...

React Interview - Advanced

Updated
View as Markdown
  1. 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 tokens

    Can 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-cookie for easier management.
    iii) State Management + Persistence Libraries
    Redux + redux-persist

    Recoil + 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
       )
     );
    
  2. 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.

  3. 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 + Suspense

     const 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-virtualized

     import { 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 Optimization

    Use responsive images, srcSet, and WebP.

    Lazy-load images using loading="lazy" or a library like react-lazyload.
    ix) Use Production Builds

    Always deploy production builds with npm run build.

    It minifies code and removes warnings, dev-only features.

  4. What are Pure Components in React?
    A Pure Component in React is a class component that automatically implements a shallow comparison in its shouldComponentUpdate() 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.

  5. 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 Computation

     const expensiveValue = useMemo(() => {
       return computeExpensiveValue(input);
     }, [input]);
    

    computeExpensiveValue only re-runs if input changes.

    Useful for expensive calculations.
    ii) useCallback – Memoize a Function

     const 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 PureComponent but 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.

  6. Explain React.memo and React.PureComponent?
    Both React.memo and React.PureComponent are optimization tools in React that help you prevent unnecessary re-renders by performing shallow comparisons of props.
    i) React.memo (Functional Components)
    React.memo is 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.PureComponent is a base class for class components that implements shouldComponentUpdate() 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).

  7. What is the React Memo Function?
    React.memo is 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.memo allows 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, Child won't re-render unless the name prop changes.
    Shallow Comparison

    React 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.memo

    i) 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 It

    i) Component renders frequently with the same props.

    ii) Component performance impacts overall app.

    iii) You're passing pure, stable props.

  8. Explain the useMemo hook and its usage?
    useMemo is 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 useMemo to replace useEffect or 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 Calculations

     const 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} />
    
  9. Explain the useCallback hook and its usage?
    The useCallback hook 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) useCallback is 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 useCallback here prevents this function from being re-created on every render.
    When Not to Use useCallback

    i) 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.

  10. 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 Thumb

    i) Use useCallback when you're passing a function as a prop and want to prevent unnecessary re-renders of memoized children.

    ii) Use useMemo when you're doing expensive calculations or deriving values from state/props.

  11. 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
    Installation

    npm install react-router-dom
    

    Use react-router-dom for web apps. Use react-router-native for 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>
      );
    }
    
  12. 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 path to match the URL and element to 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" />} />
    
  13. 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, /dashboard

    Best 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 navigation

    URLs look like /#/about

    Useful 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 memory

    Doesn’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>
    
  14. 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) Usage

    import { 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) Usage

    import { 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).

  15. 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 the location.search property combined with the URLSearchParams Web API.

    http://localhost:3000/profile?name=John&age=25
    

    Inside 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).

  16. 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 user

    ii) Store login state/token

    iii) Use useNavigate() (v6) or history.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') or history.push()
    ii) Keep previous location - Use location.state.from
    iii) Redirect after login success - Place redirect logic inside handleLogin()

  17. 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 testing

    Enables 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 predictable

    Improves 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 effects

    function 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

  18. 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 type field (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());
    });
    
  19. 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 Tools

    React:
    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 Support

    React:
    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) Scalability

    React:
    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 Testing

    React:
    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 Flow

    React:
    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.

  20. 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 Typos

    Using constants helps avoid mistakes like this,

    dispatch({ type: ADD_TODO }); // typo would cause a reference error instead
    

    ii) 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) Maintainability

    Keeping 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 Scalability

    In large teams, having constants helps developers:

    a) Understand the available actions

    b) Reuse existing ones instead of creating duplicates
    v) Integration with Tools

    Redux 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;
      }
    };
    
  21. 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 Store

    i) 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 action

    ii) 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: 1
    

    The Redux store is essential for managing and synchronizing state in larger or more complex React applications.

  22. 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 a type property (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 happening

    ii) 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 the dispatch() method

    store.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.

  23. 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) => newState
    

    Key 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 state

    const 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 using combineReducers()

    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.

  24. 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 Middleware

    Component → dispatch(action)
                 ↓
             [Middleware]
                 ↓
             Reducer → New State
    

    Logging 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 dispatched
    

    Applying Middleware
    Use Redux’s applyMiddleware() when creating the store

    import { 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.

  25. 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) Using useSelector Hook (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) Using useDispatch Hook (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) from react-redux to 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) Using store.getState() Directly (Outside React)
    You can directly call getState() on the store object, typically outside React components (e.g., in middleware or setup scripts).

    const state = store.getState();
    console.log(state);
    

    v) Using Provider to 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+), useSelector and useDispatch are the most concise and efficient ways to access the Redux store in function components.

  26. What is the difference between mapStateToProps() and mapDispatchToProps()?
    In a React-Redux application, mapStateToProps() and mapDispatchToProps() are two key functions used with the connect() 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 the dispatch function 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 connect

    import 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 store

  27. How 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 like redux-thunk or redux-saga for async
    ix) Learning Curve
    Relay - Steeper (GraphQL + Relay concepts)
    Redux - Moderate (but more intuitive than Relay)

  28. 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 point
    

    i) app/

    Central Redux config files

    Contains store.js, rootReducer.js, and optionally middleware.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/
    
  29. 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 using connect, 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 like redux-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);
    
  30. 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 Function

    import { 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

  31. 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 = initialState ensures that if the state is undefined (as it is on first run), Redux uses initialState.
    ii) Pass Initial State to the Store (optional override)
    You can also pass an initial state directly to createStore (for legacy Redux) or configureStore from 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 preloadedState if:

    You need to hydrate state from localStorage or server.

    You're writing unit tests and want to inject custom state.

  32. What are the differences between call() and put() in redux-saga?
    In redux-saga, call() and put() 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 to dispatch() 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."

  33. 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 (using reselect) 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 a selectors.js file per feature,

    features/
    ├── todos/
    │   ├── todosSlice.js
    │   ├── todosAPI.js
    │   └── todosSelectors.js
    
  34. What 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 Cases

    i) Search filters on large lists

    ii) Infinite scrolling

    iii) Animations during state updates

    iv) Lazy loading content with fallback

  35. What is forwardRef function in React?
    The forwardRef function 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 forwardRef enables this behavior explicitly.
    Only function components can be used with forwardRef (not regular arrow functions).

    You cannot use hooks inside the forwardRef call 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 ref
    

    Define a Component Using forwardRef

    import 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

  36. 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 Works

    i) 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 app

    Considerations

    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() (not useLayoutEffect()) for client-only logic to avoid SSR warnings.

  37. What is the difference between try catch block and error boundaries in React?
    In React, try...catch blocks 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 Case

    a) Used to catch synchronous errors in JavaScript code.

    b) Typically used in event handlers, functions, or any imperative logic.
    Characteristics

    a) 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 Case

    i) Used to catch rendering errors in React component trees.

    ii) Designed for React components, especially in the UI rendering lifecycle.
    Characteristics

    i) 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...catch there).

    Errors in asynchronous code like setTimeout, Promises, or fetch (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) Use try...catch for 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.

  38. 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-Type
    

    i) 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, like http://api.example.com or even localhost:5000.
    Common Scenario
    You run a React app locally

    http://localhost:3000
    

    And 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:5000 explicitly 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/data
    
  39. How 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 Securely

    Use 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 localStorage or sessionStorage if 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 env variables in .env files (like REACT_APP_SECRET) are exposed in the client bundle!
    vi) Secure Your API

    Use 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 Updates

    Keep dependencies up to date.

    Use tools like:

    npm audit

    yarn audit

    GitHub Dependabot
    viii) Enable Code Splitting and Lazy Loading

    Reduces attack surface by loading only what’s needed.

    Use React's React.lazy() and Suspense to defer loading code.
    ix) Security Headers (via Backend)
    If serving React from Node/Express

    npm install helmet
    
    const helmet = require('helmet');
    app.use(helmet());
    
  40. 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 build
    

    this minifies code

    it removes development warnings

    it also enables performance optimizations
    ii) Code Splitting with React.lazy() and Suspense

    Split 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 change

    const MyComponent = React.memo(({ data }) => { ... });
    

    useMemo() and useCallback() -
    useMemo caches expensive calculations
    useCallback caches functions to avoid unnecessary renders

    const 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 like react-window or react-virtualized

    import { 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.debounce or a custom debounce function.
    vii) Use Efficient Images and Assets
    Compress and optimize images.

    Use WebP or AVIF formats.

    Use lazy loading for images

    <img src="image.jpg" loading="lazy" alt="example" />
    
  41. 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 with npm run build for production. This: Minifies code, Removes development warnings, Optimizes React for performance
    ii) Code Splitting & Lazy Loading

    I use React.lazy() and Suspense to split code and defer loading components until they are needed

    const Dashboard = React.lazy(() => import('./Dashboard'));
    

    Reduces initial load time by loading parts of the app on demand.
    iii) Prevent Unnecessary Re-renders
    React.memo() for pure components

    useCallback() for functions passed as props

    useMemo() for expensive calculations

    const 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 useReducer for complex state logic

    Use optimized state libraries like Zustand or Jotai over bulky ones when possible
    v) Virtualize Large Lists
    When dealing with large datasets, I use react-window or react-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 Handling

    To 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 & Assets

    Use compressed images in WebP format.

    Lazy-load images with the loading="lazy" attribute.

    Use SVGs for simple icons/logos.
    viii) Minimize Bundle Size

    I analyze the bundle with source-map-explorer or webpack-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 Workers

    For progressive apps,

    I set up service workers with tools like Workbox.

    Cache static assets, fonts, and API responses.
    x) Monitor and Audit Performance

    I regularly use,

    React DevTools to inspect component renders.

    Chrome Lighthouse for performance scores.

    Web Vitals to measure real user metrics.

  42. 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 DevTools

    Right-click anywhere on the page → click Inspect, or

    Press Ctrl + Shift + I (Windows/Linux) or Cmd + Option + I (Mac)
    iii) Go to the "Lighthouse" Tab

    In 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 Type

    Choose Mobile (default) or Desktop

    Mobile emulation simulates a slower device/network — good for real-world performance insights.
    vi) Run the Audit

    Click "Analyze page load" or "Generate report".

    Lighthouse will reload your page, run a series of tests, and then show a detailed report.

  43. 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 build
    

    This does the following:

    Sets process.env.NODE_ENV to '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 build
    

    ii) Using Vite
    Run the production build command,

    npm run build
    

    This creates an optimized build in the dist/ folder.

    To preview it

    npm run preview
    

    iii) Using Webpack (Custom Setup)
    In your Webpack config,

    mode: 'production'
    

    Then run your custom production build,

    webpack --mode production
    

    Why 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=production

  44. Whar 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)
    Pros

    a) 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) Netlify

    Pros

    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 Pages

    Pros

    a) Free and simple for static sites

    Cons

    a) Only works for static builds

    Usage

    a) Build the app: npm run build

    b) Use the gh-pages package to push the build/ folder

    c) Add "homepage": "https://<username>.github.io/<repo>" to package.json
    iv) Firebase Hosting

    Pros

    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-tools

    b) Initialize: firebase init

    c) Deploy: firebase deploy
    v) AWS Amplify

    Pros

    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 Deployment

    i) 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)

  45. 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 Compiler

    A new compiler that automatically optimizes your code, eliminating the need for manual memoization with useMemo, useCallback, or memo. This leads to cleaner code and improved performance.
    ii) Server Components

    Enables rendering parts of your UI on the server, reducing client-side JavaScript and improving performance and SEO.
    iii) New Hooks

    useActionState: 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() API

    A new hook that simplifies data fetching by allowing you to await promises directly within components, reducing boilerplate code.
    v) Actions API

    Simplifies asynchronous state updates by integrating async functions directly into React's rendering cycle, reducing the need for complex state management logic.
    vi) Enhanced Concurrent Rendering

    Improvements to React's concurrent rendering capabilities, including better scheduling and automatic batching of state updates, leading to more responsive applications.
    vii) Simplified Ref Handling

    You can now pass refs as regular props, making it easier to work with custom components and reducing the need for forwardRef.
    viii) Native Metadata Rendering

    Support 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.0
    

    Before upgrading, it's recommended to update to React 18.3.1 to identify any deprecated APIs and prepare your codebase for the transition.

  46. 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 Version

    npm list react
    npm list react-dom
    

    ii) 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@latest
    

    Or with Yarn

    yarn add react@latest react-dom@latest
    

    iv) 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 TypeScript
    

    Also update testing libraries if needed

    npm install @testing-library/react @testing-library/jest-dom
    

    v) Update browserslist

    Modern React may rely on updated browser features:
    In package.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.rendercreateRoot)

    Use the React 19 Migration Guide
    vii) Test Thoroughly

    npm run build
    npm test
    npm start
    
  47. How 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-Escapes

    React automatically escapes values in JSX,

    <div>{userInput}</div> // Safe
    

    b) 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 comands

    npm audit fix
    npm audit
    

    iii) Secure API Communication
    a) Use HTTPS for all requests

    Enforce 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; use HttpOnly cookies 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 helmet
    
    app.use(require("helmet")());
    

    v) Prevent Cross-Site Request Forgery (CSRF)
    If using cookies for auth, implement CSRF tokens

    Use 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 Updated

    a) Use latest versions of React, React DOM, and other packages

    b) Monitor official changelogs for security patches
    viii) Limit Exposure of Build Info

    a) Avoid exposing: Build metadata, Version numbers, Stack traces

    b) Set NODE_ENV=production and configure error boundaries.
    ix) Use Error Boundaries
    Prevent exposing internal logic to users

  48. Explain 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 Webpack

    i) 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.

    HtmlWebpackPlugin

    CleanWebpackPlugin

    MiniCssExtractPlugin
    v) Mode: Either development or production

    mode: 'development'  // or 'production'
    

    Webpack in React
    i) Why It’s Used

    a) 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 Example

    A Node.js + Express server with Webpack to serve a React frontend:

    a) React app → Bundled by Webpack

    b) Served by Express from /dist