Top React Interview Questions

ReactPerformance BottlenecksOptimization

Below are some common bottlenecks.

  1. Unnecessary re-renders due to state/props changes.
  2. Large component trees with no memoization.
  3. Passing inline functions or objects causing useEffect or useMemo to re-run.
  4. Improper key usage in lists.


How to avoid them?

  1. Use React.memo, useMemo, useCallback.
  2. Use correct keys in lists.
  3. Lazy-load components (React.lazy, dynamic in Next.js).
  4. Split large components.
  5. Optimize context usage.

ReactProp DrillingState Management

Use Context API for global/shared data.


Use state management libraries (Redux, Zustand, Jotai).


Compose components smartly (e.g., compound components).

ReactReconciliationRendering

Reconciliation is the process React uses to update the DOM. It compares the new virtual DOM with the previous one (diffing) and applies minimal changes to the real DOM.
Key concepts:
React uses heuristics (like key) to match elements efficiently.


It avoids re-rendering the entire tree.

ReactList RenderingPerformance

When handling long lists in React, performance optimization is crucial to ensure a smooth user experience. Several techniques can be employed:


Virtualization/Windowing:
This technique renders only the visible portion of the list, significantly reducing the number of DOM nodes and improving performance. Libraries like react-window and react-virtualized facilitate this approach.


Pagination:
Dividing the list into smaller, manageable pages allows users to load data incrementally, preventing the browser from rendering a large number of elements at once.


Infinite Scrolling:
Loading more items as the user scrolls down provides a seamless experience while avoiding rendering the entire list upfront.


Keys:
Providing unique keys for each list item helps React efficiently update the DOM when items are added, removed, or reordered.


Conditional Rendering:
Only rendering necessary components based on conditions can prevent unnecessary rendering cycles.

 


Memoization:
Using React.memo for functional components or shouldComponentUpdate for class components can prevent re-renders if props haven't changed.


Code Splitting:
Deferring the loading of non-essential code can improve initial load time, especially when dealing with large datasets.

 


Choosing the appropriate technique depends on the specific requirements of the application and the nature of the data being displayed. Combining multiple techniques can yield the best results for handling extremely long lists in React.

ReactAsynchronous RequestsData Fetching

Use Promise.all() to run requests in parallel.


Use tools like React Query, SWR, or Redux Toolkit Query.


Manage loading, error, and data states cleanly.

ReactAuthenticationSecurityPersistence

To persist authentication securely in a React app, the following methods are commonly used:
HTTP-only cookies:
Upon successful authentication, the server sends a session ID stored in an HTTP-only cookie. This type of cookie is inaccessible to JavaScript, mitigating the risk of XSS attacks. The server can then verify the session ID on subsequent requests.
Local Storage/Session Storage with caution:
While it's possible to store tokens in local storage or session storage, it's crucial to implement extra security measures due to the risk of XSS attacks. Consider encrypting the token before storing it and implementing strict input validation to prevent malicious scripts from accessing it.
Context API or Redux:
For managing authentication state within the application, Context API or Redux can be used. However, these should not be used as the sole method for persisting authentication, as the state will be lost on page refresh. They should be used in conjunction with cookies or local storage.
Using a dedicated authentication library:
Libraries such as react-auth-kit or Auth0 provide pre-built solutions for handling authentication, including persistence. These libraries often handle the complexities of token management and security best practices.


It's important to always use HTTPS to encrypt data transmitted between the client and server and implement strong input validation to prevent injection attacks. Multi-factor authentication can also be used to enhance security.
 

ReactProject StructureScalability
  1. Feature-based folder structure (e.g., /features/auth, /features/dashboard).
  2. Use domain-driven design if appropriate.
  3. Maintain clear boundaries: components, hooks, contexts, services, utils.
  4. Use Barrel files for re-exports.
  5. Incorporate Redux Toolkit / Zustand / Context API for state management.

src/
 ??? features/
     ??? user/
         ??? components/
         ??? hooks/
         ??? services/
         ??? UserPage.tsx
 

ReactControlled ComponentsUncontrolled ComponentsForms

In React, controlled and uncontrolled components represent two distinct ways of handling form data. Controlled components use React's state to manage form data, while uncontrolled components rely on the DOM to handle it. This difference primarily affects how data is tracked and updated within the component.

 

Uncontrolled Components
Uncontrolled Components are the components that do not rely on the React state and are handled by the DOM. So, in order to access any value that has been entered, we take the help of refs.

import React, { useRef } from "react";
function App() {
   const inputRef = useRef(null);
   function handleSubmit() {
       console.log(`Name: ${inputRef.current.value}`);
   }
   return (
       <div className="App">
           <form onSubmit={handleSubmit}>
               <label>Name :</label>
               <input
                   type="text"
                   name="name"
                   ref={inputRef}
               />
               <button type="submit">Submit</button>
           </form>
       </div>
   );
}
export default App;

Controlled Components
In React, Controlled Components are those in which form’s data is handled by the component’s state. It takes its current value through props and makes changes through callbacks like onClick, onChange, etc. A parent component manages its own state and passes the new values as props to the controlled component.

import { useState } from "react";
function App() {
   const [name, setName] = useState("");
   function handleSubmit() {
       console.log(`Name: ${name}`);
   }
   return (
       <div className="App">
           <form onSubmit={handleSubmit}>
               <label>Name:</label>
               <input
                   name="name"
                   value={name}
                   onChange={(e) =>
                       setName(e.target.value)
                   }
               />
               <button type="submit">Submit</button>
           </form>
       </div>
   );
}
export default App;

Controlled components are preferred in complex forms for validation and dynamic updates.

ReactuseMemouseCallbackHooks

The key difference between useMemo and useCallback in React lies in what they memoize: useMemo memoizes the result of a function, while useCallback memoizes the function itself. useMemo is used when you have an expensive calculation you want to avoid on every render, and useCallback is used to prevent a function from being recreated on every re-render, which can be important when passing functions as props to child components. 

useMemo:
Memoizes the result of a function (the value returned by the function).


Only recomputes the function's result if the dependencies in the dependency array change.


Useful for optimizing performance when you have expensive calculations that don't need to be re-run on every render.


Example: Memoizing the result of a complex calculation involving a large dataset.
useCallback:
Memoizes the function itself, meaning it returns the same function object unless the dependencies in the dependency array change.


Prevents child components from re-rendering unnecessarily when the function is passed as a prop.


Useful when dealing with functions that are frequently passed as props to child components, especially those that rely on frequently changing props or state.


Example: Memoizing a function that handles form submissions and passing it as a prop to a child component, to prevent the child component from re-rendering when the parent component re-renders. 

ReactCompound ComponentsComponent Composition

Compound components are a pattern where multiple components work together, sharing internal state.

Example:

<Tabs>
 <Tabs.Tab label="Tab 1">Content 1</Tabs.Tab>
 <Tabs.Tab label="Tab 2">Content 2</Tabs.Tab>
</Tabs>

Compound components are particularly useful in situations where you need to create UI elements that are both complex and reusable. By leveraging this pattern, you can write more maintainable and scalable code

ReactConcurrent ModeRendering

Concurrent Mode in React is a set of new features that help React apps stay responsive even when rendering large components or handling complex updates. It allows React to interrupt rendering tasks, work on high-priority updates first, and resume or abandon less critical tasks as needed. This makes the user interface feel smoother and more responsive, especially during time-consuming rendering operations.


Key Features and Concepts
Interruptible Rendering:
React can pause rendering to handle other events, like user input, ensuring the UI remains responsive.
Prioritizing Updates:
React can prioritize updates, processing important ones first and deferring less urgent ones.
Time Slicing:
Rendering work is broken into smaller chunks, allowing React to pause and resume rendering without blocking the main thread.
Suspense:
Components can "suspend" rendering while waiting for data, displaying a fallback UI until the data is ready.
Transitions:
Help differentiate between urgent and non-urgent updates, ensuring the UI remains responsive during transitions.
Automatic Batching:
Multiple state updates are grouped into a single re-render, improving performance. 


Benefits of concurrent mode
Improved User Experience:
Smoother and more responsive UI, especially during complex rendering tasks.
Better Performance:
Efficient handling of updates and rendering, leading to faster initial render times.
Enhanced Error Handling:
Improved error boundaries for handling errors during rendering, suspending, or resuming components. 


How it Works?
Concurrent Mode introduces a new rendering mechanism that allows React to work on multiple tasks concurrently. It does not mean that state updates are executed in parallel, but rather that they can overlap, with React prioritizing tasks based on their urgency.

 

import React, { useState, useTransition } from 'react';
function MyComponent() {
 const [isPending, startTransition] = useTransition();
 const [count, setCount] = useState(0);
 const handleClick = () => {
   startTransition(() => {
     setCount(c => c + 1);
   });
 };
 return (
   <div>
     {isPending && <p>Updating...</p>}
     <p>Count: {count}</p>
     <button onClick={handleClick}>Increment</button>
   </div>
 );
}

In this example, useTransition is used to mark the state update as non-urgent. If other urgent updates occur while this update is being processed, React can prioritize them, ensuring the UI remains responsive.

ReactMemory LeaksPerformance

React itself doesn't inherently prevent memory leaks, but it provides tools and patterns to manage component lifecycles effectively, which can help avoid them. Memory leaks in React applications typically occur when resources are not properly released when components unmount.


To prevent memory leaks, we should follow the best practices below.
Use the Cleanup Function in useEffect:
This function allows you to execute code when a component unmounts, which can be used to clear timers, unsubscribe from events, and cancel API requests.
Clear Timers and Intervals:
Always clear timers and intervals using clearTimeout and clearInterval within the cleanup function of useEffect or in the componentWillUnmount lifecycle method (for class components).
Cancel API Requests:
Use an AbortController to cancel ongoing API requests when a component unmounts.
Remove Event Listeners:
Remove event listeners using removeEventListener within the cleanup function of useEffect or in the componentWillUnmount lifecycle method.
Avoid Global References:
Be mindful of storing references in global objects or contexts that are not cleaned up properly.
Use Strict Mode:
Enable strict mode in your JavaScript code to help catch potential issues that could lead to memory leaks.

 

Example:

useEffect(() => {
 const controller = new AbortController();
 fetch('url', { signal: controller.signal });
 return () => controller.abort(); // Cleanup
}, []);

ReactControlled ComponentsUncontrolled ComponentsForms

In React, controlled and uncontrolled components represent two distinct ways of handling form data. Controlled components use React's state to manage form data, while uncontrolled components rely on the DOM to handle it. This difference primarily affects how data is tracked and updated within the component.

 

Uncontrolled Components
Uncontrolled Components are the components that do not rely on the React state and are handled by the DOM. So, in order to access any value that has been entered, we take the help of refs.

import React, { useRef } from "react";
function App() {
   const inputRef = useRef(null);
   function handleSubmit() {
       console.log(`Name: ${inputRef.current.value}`);
   }
   return (
       <div className="App">
           <form onSubmit={handleSubmit}>
               <label>Name :</label>
               <input
                   type="text"
                   name="name"
                   ref={inputRef}
               />
               <button type="submit">Submit</button>
           </form>
       </div>
   );
}
export default App;

Controlled Components
In React, Controlled Components are those in which form’s data is handled by the component’s state. It takes its current value through props and makes changes through callbacks like onClick, onChange, etc. A parent component manages its own state and passes the new values as props to the controlled component.

import { useState } from "react";
function App() {
   const [name, setName] = useState("");
   function handleSubmit() {
       console.log(`Name: ${name}`);
   }
   return (
       <div className="App">
           <form onSubmit={handleSubmit}>
               <label>Name:</label>
               <input
                   name="name"
                   value={name}
                   onChange={(e) =>
                       setName(e.target.value)
                   }
               />
               <button type="submit">Submit</button>
           </form>
       </div>
   );
}
export default App;

Controlled components are preferred in complex forms for validation and dynamic updates.

ReactRender PropsHigher-Order ComponentsComponent Composition

Render Props and Higher-Order Components (HOCs) are both techniques in React for sharing reusable logic, but they differ in how they achieve this. Render Props pass a function as a prop to a component, allowing it to control the rendering within that component, while HOCs are functions that take a component as an argument, wrap it, and return a new component with additional logic. Render Props offer a more explicit way to share logic and can be easier to understand in some cases, but HOCs can provide more modularity and can be beneficial for certain scenarios. 

 

Render Props: A component takes a function as a prop, which it then calls within its render method to determine what to render.

Example:

   import React from 'react';
   function RenderPropButton({ children }) {
     return (
       <button onClick={children}>
         Click Me
       </button>
     );
   }
   // Usage
   function Counter() {
     const [count, setCount] = React.useState(0);
     return (
       <RenderPropButton
         children={e => {
           setCount(count + 1);
           return "Click count";
         }}
       />
     );
   }

    

Higher-Order Components (HOCs): A function that takes a component as an argument, wraps it with additional logic, and returns a new, enhanced component.

Example: 

  import React from 'react';
   function withAuth(WrappedComponent) {
     return props => {
       const isLoggedIn = true; // replace this with auth logic
       return (
         <WrappedComponent
           {...props}
           isLoggedIn={isLoggedIn}
         />
       );
     };
   }
   import React from 'react';
   import withAuth from './withAuth';
   function ProtectedComponent({ isLoggedIn }) {
     return (
       <div>
         {isLoggedIn ? (
           <p>You are logged in!</p>
         ) : (
           <p>Please log in.</p>
         )}
       </div>
     );
   }

    Usage:

   const AuthenticatedComponent = withAuth(ProtectedComponent);

Render Props are a powerful and flexible way to share logic and rendering control between components, but they can become more complex with multiple nested props. HOCs offer a more modular and reusable approach for specific scenarios, but they can also become harder to reason about when they are nested. React's official documentation and the introduction of Hooks have provided alternative solutions that can be simpler and more direct in many cases. 

ReactPerformance OptimizationLarge Applications

To optimize performance in a large-scale React app, focus on reducing unnecessary re-renders, minimizing bundle sizes, and improving rendering of large lists. This includes using techniques like memoization, code splitting, lazy loading, virtualization, and React Profiler.

 

1. Minimize Re-renders:

React.memo(): Prevent components from re-rendering when their props haven't changed.

useMemo and useCallback Hooks: Optimize expensive calculations and function definitions.

Avoid inline function definitions: Inline functions can cause unnecessary re-renders, so define them outside the render method.

Properly use keys when rendering lists: Unique keys help React efficiently update the DOM.

 

2. Reduce Bundle Size:

Code splitting: Divide large bundles into smaller chunks loaded on demand.

Lazy loading: Load components only when they are needed.

Tree shaking: Remove unused code from bundles.

Minification and compression: Reduce the size of JavaScript and CSS files.

Optimize image loading: Use lazy loading and optimize image formats.

 

3. Improve Rendering of Large Lists:

List virtualization: Render only the visible items in a list, significantly improving performance for very large datasets.

Windowing libraries:Use libraries like react-window or react-virtualized to facilitate list virtualization.

 

4. Other Performance Tips:

Server-Side Rendering (SSR):Render React components on the server and send pre-rendered HTML to the client, improving initial load time and SEO.

React Profiler:Use the React Profiler to identify performance bottlenecks and optimize re-renders.

Use immutable data structures:Immutable data structures can make it easier to detect when data has changed, preventing unnecessary re-renders.

Throttling and debouncing:Limit the frequency of event handlers, especially for events like scrolling and resizing.

Web workers:Offload CPU-intensive tasks to web workers to improve performance.

Modular architecture:

Break down the application into smaller, self-contained components for better organization and code reusability.

 

By implementing these techniques, you can significantly improve the performance of your large-scale React application and provide a smoother user experience. 

ReactProject StructureScalabilityTeam Development

To structure a React project for scalability in a team, focus on a modular architecture with well-defined folders for components, pages, services, and potentially a store for state management. Consider using a monorepo for managing multiple packages/features if needed, and organize files by feature to enable parallel development and reduce merge conflicts. 


We can follow the practices below.
1. Modular Architecture & Folder Structure:
Components:
Organize components by feature or logical grouping (e.g., home, login, product-description, shared). 
Each feature folder can contain the component's code, styles, tests, and potentially related utilities. 
 Pages:
If the project has distinct pages (e.g., login page, home page), create a pages folder to organize them. 
Pages can be composed of multiple components, making them reusable. 
 Services:
Place code related to data fetching, API calls, and other external interactions in a services folder. 
This helps isolate data handling logic from component logic. 
 Store (for state management):
If using Redux or a similar state management library, organize actions, reducers, and potentially selectors in a separate store or state folder. 
Consider using subfolders within the store for different features. 
 Utils:
Create a utils folder to store reusable functions that are not specific to any feature or component. 
 Assets:
Place images, icons, and other static assets in an assets folder. 
 Styles:
Store CSS or SCSS files in a styles folder. 
Consider using CSS modules or other techniques to manage styles effectively. 
2. Monorepo (if needed):

  • If the project becomes large and complex, consider using a monorepo to manage multiple packages or features.
  • A monorepo allows independent development and deployment of features within separate packages.
  • Tools like Yarn workspaces or npm workspaces can be used to manage packages within a monorepo. 


3. Feature-Driven Development:

  • Organize files and folders by feature or logical unit, making it easier to identify related code and manage changes.
  • Each feature can have its own folder containing components, styles, tests, and potentially related utilities.
  • This approach allows developers to work on different features independently, reducing merge conflicts and improving maintainability. 

     

4. State Management:

  • Use a state management library like Redux or Context API to manage the application's state.
  • Choose the appropriate state management approach based on the project's complexity and requirements.
  • For larger projects, consider using Redux or other specialized state libraries as they offer better control and scalability. 


5. Testing:

  • Write unit tests for components and integration tests for features.
  • Use testing libraries like Jest and React Testing Library to automate testing.
  • Testing helps ensure code quality, maintainability, and reduces the risk of bugs. 

ReactAccessibilityWeb Standards

To ensure accessibility in a React app, focus on semantic HTML, keyboard navigation, ARIA attributes, and visual feedback. Use semantic HTML elements like <button> and <input> instead of generic <div> or <span>. Ensure all interactive elements can be operated with a keyboard and offer clear visual cues for focus. 


Key Accessibility Practices in React:
Semantic HTML:
Utilize semantic HTML elements to define the purpose of content. This helps assistive technologies like screen readers understand the structure and content of your React application. 
Keyboard Navigation:
Make sure users can navigate and interact with your app using only the keyboard. This includes proper focus management and visual cues for focused elements. 
ARIA Attributes:
Use ARIA (Accessible Rich Internet Applications) attributes to add context and information to your elements, especially when using non-semantic HTML or when you need to provide additional information for assistive technologies. 
Visual Feedback:
Provide clear visual feedback for focused elements, such as a distinct outline or color change, to help users understand where they are in the navigation. 
Color Contrast:
Ensure sufficient contrast between text and background colors to make your content readable for users with low vision. 
Alt Text:
Provide descriptive alt text for all images, so users who use screen readers can understand the content of images. 
Accessibility Testing:
Regularly test your React application for accessibility issues using tools like axe-core or react-a11y to ensure compliance with accessibility standards like WCAG. 

ReactuseEffectuseLayoutEffectHooks

The primary difference between useEffect and useLayoutEffect lies in their timing of execution and how they interact with the browser. useEffect runs after the browser has painted the screen, making it asynchronous and non-blocking. useLayoutEffect, on the other hand, runs synchronously after DOM mutations but before the browser paints, which can block the browser from updating until it's complete. 


Below is the comparision between both:
1. Timing:
useEffect: Runs asynchronously after the browser has painted the component to the screen. This means the user has already seen the component before the useEffect hook's code executes. 
useLayoutEffect: Runs synchronously after all DOM mutations but before the browser updates the screen (before painting). This means that the code inside useLayoutEffect will run before the user sees the updated component. 


2. Performance:
useEffect: Because it runs asynchronously, useEffect does not block the browser from rendering the component. This makes it more performant in most cases, especially for tasks like fetching data or setting up subscriptions. 
useLayoutEffect: Because it runs synchronously, useLayoutEffect can potentially block the browser from painting the component. While it can be useful for preventing layout thrashing or ensuring immediate DOM measurements, using it for non-essential tasks can impact performance. 


3. Use Cases:
useEffect: Ideal for side effects that don't require immediate DOM updates, such as fetching data, setting up subscriptions, or updating the component's state. 
useLayoutEffect: Useful for side effects that require synchronous updates, like measuring the dimensions of a component or manipulating the DOM directly to prevent layout issues or screen flickering. 


Choose useEffect when you don't need to directly interact with the DOM or when you want to avoid blocking the browser. Choose useLayoutEffect when you need to manipulate the DOM synchronously before the browser paints, for example, to prevent layout shifts or ensure accurate measurements. 

ReactMemory LeaksWebSocketEvent Listeners

To handle memory leaks in React components that use WebSockets and event listeners, it is crucial to properly manage the lifecycle of these connections. The useEffect hook is commonly used for this purpose.
When a component mounts and establishes a WebSocket connection or adds an event listener, a cleanup function should be returned from the useEffect hook. This function will be executed when the component unmounts or before the effect is re-run due to dependency changes. Within the cleanup function, the WebSocket connection should be closed using websocket.close() and event listeners should be removed using removeEventListener. This ensures that these resources are released when they are no longer needed, preventing memory leaks.


Here's an example demonstrating how to handle memory leaks with WebSockets:

import React, { useState, useEffect, useRef } from 'react';
function WebSocketComponent() {
 const [messages, setMessages] = useState([]);
 const websocket = useRef(null);
 useEffect(() => {
   websocket.current = new WebSocket('ws://your-websocket-url');
   websocket.current.onopen = () => {
     console.log('WebSocket connected');
   };
   websocket.current.onmessage = (event) => {
     setMessages((prevMessages) => [...prevMessages, event.data]);
   };
   websocket.current.onclose = () => {
     console.log('WebSocket disconnected');
   };
   websocket.current.onerror = (error) => {
     console.error('WebSocket error:', error);
   };
   return () => {
     if (websocket.current) {
       websocket.current.close();
       console.log('WebSocket connection closed');
     }
   };
 }, []);
 return (
   <div>
     <h2>WebSocket Messages</h2>
     <ul>
       {messages.map((message, index) => (
         <li key={index}>{message}</li>
       ))}
     </ul>
   </div>
 );
}
export default WebSocketComponent;

 

Here's an example demonstrating how to handle memory leaks with event listeners:

import React, { useEffect } from 'react';
function EventListenerComponent() {
 const handleScroll = () => {
   console.log('Scrolled');
 };
 useEffect(() => {
   window.addEventListener('scroll', handleScroll);
   return () => {
     window.removeEventListener('scroll', handleScroll);
   };
 }, []);
 return (
     <div style={{height: "200vh", overflow: "auto"}}>
       <p>Scroll down to see the effect.</p>
     </div>
 );
}
export default EventListenerComponent;

By implementing these cleanup mechanisms, memory leaks associated with WebSockets and event listeners can be effectively prevented in React applications.

ReactReact ProfilerPerformance Optimization

The React Profiler is a tool built into React's developer tools that measures the render time of React components. It helps to identify performance bottlenecks by providing detailed information on how long each component takes to render and how often it re-renders.

In optimizing a React application, the Profiler can be used to identify components that are taking a long time to render or re-rendering unnecessarily. After identifying these performance bottlenecks, optimization techniques such as memoization with React.memo, useMemo, and useCallback can be applied to prevent unnecessary re-renders. Code splitting and lazy loading can also be implemented to reduce the initial load time of the application. After applying these optimizations, the Profiler can be used again to measure the impact of the changes and ensure that the application's performance has improved.

ReactState ManagementNested StateForms

We can use state normalization or libraries like Formik, React Hook Form, or zustand.

// Zustand store
const useStore = create((set) => ({
 formData: {},
 updateField: (field, value) =>
   set((state) => ({
     formData: { ...state.formData, [field]: value },
   })),
}));

ReactServer ComponentsPerformance

React Server Components (RSC) allow developers to build components that are rendered on the server and sent to the client as static HTML, reducing client-side JavaScript bundle size.

 

Example Use-Case:

In a B2B dashboard app with heavy data (charts, reports), rendering static data-intensive components like analytics reports as Server Components reduces initial JS load and improves FCP (First Contentful Paint).

 

// Server Component in app/dashboard/page.tsx
export default async function DashboardPage() {
 const data = await fetchAnalyticsData(); // Fetch server-side
 return (
   <div>
     <h1>Dashboard</h1>
     <ChartComponent data={data} />
   </div>
 );
}

ReactCode SplittingScalability

In a large React application, code splitting is implemented by using dynamic imports with React.lazy() and the <Suspense> component. This allows components to be loaded on demand, reducing the initial bundle size and improving performance. 



1. Dynamic Imports: 
Use the import() function to import modules dynamically, instead of statically at the top of a file.
This tells the bundler to split the code into separate chunks, which are then loaded on demand.
2. React.lazy(): 
React's lazy() function simplifies lazy loading of components.
It takes a dynamic import as an argument, returning a component that is lazily loaded.
3. <Suspense> Component: 
The <Suspense> component provides a loading indicator while a lazily loaded component is being fetched.
It allows you to specify a fallback UI that is displayed during the loading process.

 

How it works: 

  • When a component is lazy-loaded, the browser starts fetching the corresponding JavaScript bundle in the background.
  • While the component is being loaded, the <Suspense> component's fallback UI is displayed.
  • Once the component is loaded, the <Suspense> component renders the lazily loaded component.
  •  
import React from 'react';
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
 return (
   <Suspense fallback={<div>Loading...</div>}>
     <LazyComponent />
   </Suspense>
 );
}
export default App;

Benefits of Code Splitting:


Reduced Initial Load Time:
By loading only the necessary code for the initial page load, the application starts faster.
Improved User Experience:
Faster loading times lead to a smoother and more responsive user experience.
Better Performance:
Smaller bundles can be cached more efficiently, leading to faster subsequent page loads.
Reduced Bandwidth Consumption:
Users download only the code they need, saving bandwidth and data costs. 


We can follow the points below for large applications.


Route-Based Code Splitting:
For applications with multiple routes, split the code based on the specific route being navigated. 
Feature-Based Code Splitting:
Divide the application into distinct features and split the code accordingly. 
Third-party Libraries:
Consider splitting code based on the usage of third-party libraries. 
Bundler Configuration:
If using a custom bundler (like Webpack), you may need to configure it to support code splitting. 
Monitoring and Measurement:
Use tools like Webpack bundle analyzer or Chrome DevTools to measure the impact of code splitting. 

ReactConcurrent RenderinguseTransition

Concurrent rendering in React allows interruptible rendering work to prioritize more critical updates (like typing) over others (like rendering a huge list). This is essential in complex UIs.

useTransition is used when you want to mark updates as non-urgent:

const [isPending, startTransition] = useTransition();
const [input, setInput] = useState("");
const handleChange = (e) => {
 const value = e.target.value;
 setInput(value);
 startTransition(() => {
   // expensive state update
   filterItems(value);
 });
};

This prevents UI jank when filtering large lists.

ReactHooks RulesCode Quality
  • Only call hooks at the top level of a component.
  • Only call hooks from React functions.


To enforce these rules, we can use eslint-plugin-react-hooks:

npm install eslint-plugin-react-hooks

In .eslintrc, use the configuration below:

"plugins": ["react-hooks"],
"rules": {
 "react-hooks/rules-of-hooks": "error",
 "react-hooks/exhaustive-deps": "warn"
}

 

ReactHydrationServer-Side Rendering

Hydration is when client-side React takes over static HTML from SSR and attaches event listeners.
Pitfalls:

  • Mismatched content between server and client causes hydration errors.
  • Avoid non-deterministic rendering like using Math.random() or Date.now() during SSR.

ReactMicro-FrontendsArchitecture

To implement micro-frontend architecture in a React app, you'll break down your application into smaller, self-contained modules, or micro-frontends, which can then be deployed and updated independently. This involves creating separate React apps for each micro-frontend, integrating them into a host application using techniques like Module Federation or iframe-based integration, and managing communication and styling consistency between them. 

ReactState ManagementNested State

Use libraries like immer to handle deep state immutably, or consider normalization using tools like normalizr.

import produce from "immer";
const newState = produce(state, draft => {
 draft.user.profile.address.city = "New York";
});

This keeps immutability intact while simplifying deep updates.

ReactState UpdatesRendering

 React throws an error:
 "Rendered more hooks than during the previous render."
You should not update state inside render because it causes infinite re-renders:

 

 Wrong:

const MyComponent = () => {
  const [val, setVal] = useState(0);
  setVal(1); // Bad
  return <div>{val}</div>;
};

 Instead of this, we should use useEffect.

ReactReact FiberRendering

React Fiber is a reimplementation of React's core reconciliation algorithm, introduced in React 16. It aims to improve performance, especially for complex applications, by enabling incremental rendering. Instead of performing updates in one go, Fiber breaks down rendering into smaller, manageable units of work. This allows React to pause, prioritize, and resume work as needed, leading to smoother user experiences.
                                  
The following are the key features of React fiber.

Incremental Rendering:

It splits rendering work into chunks, allowing React to pause and resume, ensuring smoother UIs.

Prioritization:

Fiber enables React to prioritize updates, giving precedence to urgent tasks like user interactions.

Concurrency:

It lays the groundwork for concurrent mode, allowing React to handle multiple updates simultaneously without blocking the main thread.

Work Loop:

Fiber introduces a work loop that processes units of work in a cooperative scheduling manner, allowing pausing and resuming work.

 

React Fiber represents the nodes of the DOM tree as fiber nodes, linked together to form a fiber tree. This structure mirrors the component tree and facilitates efficient updates.

ReactCustom HookscomponentDidMount
import { useEffect } from 'react';
function useComponentDidMount(callback) {
 useEffect(() => {
   callback();
   // Empty dependency array ensures this runs once
 }, []);
}
// Usage
useComponentDidMount(() => {
 console.log('Component mounted');
});