Skip to main content

Welcome to another article about our Frontend Interview Exercises Series, where we help acing frontend development one concept at a time. We will take a look in the creation of React Virtualized Lists, a common exercise you might encounter during a live coding session. So, let’s jump right into it.

Note: We are creating FrontSage to help you ace your frontend-interviews. Subscription to the waiting list are now open, limited spots are available, give it a look!

Deciphering Virtualization:

Let’s start from an example, you have a list of 1000 items and the container will just show 10 items at a time, what happens with virtualization is:

  • Only 10 items are rendered and interacted with by the DOM.
  • As you scroll, the previously rendered items are replaced by the new visible items.
  • Placeholder elements represent the 990 items that are not currently visible, maintaining the structure and scrollbar.

Core concepts

  • Visible Items Only: Instead of rendering all items in a list, virtualization will help you render only a portion of items that will be visible within the viewport (the visible area of the container).
  • Reduced DOM Nodes: This will lead to smaller number of DOM nodes that need to be created, updated, and managed, improving performance, especially for long lists.
  • Start and End items: we will get the start and end indices of the items that need to be rendered based on various factors like, current scroll position, container height, and item height, we will then slice keeping only the ones that are between the calculated range.

3. Placeholder Elements:

  • Maintaining Structure: Placeholder elements (usually empty divs with a set height) are used to replace the non-visible items. This maintains the overall structure and scrollability of the list.
  • Correct Scrollbar: The placeholders ensure that the scrollbar represents the entire length of the list, even though only a fraction of the list is rendered.

4. Scroll Event Handling:

  • User Interaction: Virtualization involves adding scroll event listeners to the container. When a user scrolls, the event listener triggers a re-calculation of the visible items.
  • Re-rendering: Based on the new scroll position, a different subset of items is rendered, and the user sees the content changing as they scroll through the list.

5. State Management:

  • Tracking Scroll Position: The scroll position is tracked using state management (for example, using React’s useState). This state determines which subset of items needs to be rendered.
  • Reactivity: The component reacts to changes in scroll position and efficiently updates the rendered items in real-time.

Crafting the InfiniteScrollList Component:

Start by creating a functional component that receives an array of items as its prop.

const items = [{
  id: 1,
  content: "content 1"
},
{
  id: 2,
  content: "content 2"
},
{
  id: 3,
  content: "content 3"
}]

function InfiniteScrollList({ items }) {
  return (
    <div>
      {items.map((item) => (
        <div key={item.id}>{item.content}</div>
      ))}
    </div>
  );
}

Selective Item Rendering:

Incorporate itemHeight and containerHeight properties to discern and render only the visible items. Initialize the scrollTop state to track the scroll position.

function InfiniteScrollList({ items, itemHeight, containerHeight }) {
  const [scrollTop, setScrollTop] = useState(0);
  // Further calculations will be done here
}

Calculating Indices and Visible Items

Calculate the startIndex and endIndex to determine which items are currently visible. Slice the items array to get the visibleItems.

const totalHeight = items.length * itemHeight;
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
  startIndex + Math.ceil(containerHeight / itemHeight),
  items.length - 1
);

const visibleItems = items.slice(startIndex, endIndex + 1);

Integrating Placeholders

Maintain the structure by calculating the offsetTop and using a placeholder div with the correct total height

const offsetTop = startIndex * itemHeight;

Handling Scroll Events

Attach a scroll event listener to update the scrollTop state, triggering a re-render with the new set of visible items.

const handleScroll = (event) => {
  setScrollTop(event.currentTarget.scrollTop);
};

Rendering the Component

Combine all the steps and render the component with the correct styling and structure.

return (
  <div
    style={{ height: `${containerHeight}px`, overflowY: 'scroll' }}
    onScroll={handleScroll}
  >
    <div style={{ height: `${totalHeight}px`, position: 'relative' }}>
      <div style={{ position: 'absolute', top: `${offsetTop}px` }}>
        {visibleItems.map((item) => (
          <div key={item.id} style={{ height: `${itemHeight}px` }}>
            {item.content}
          </div>
        ))}
      </div>
    </div>
  </div>
);

Final Code

import React, { useState } from "react";
const items = [{
  id: 1,
  content: "content 1"
},
{
  id: 2,
  content: "content 1"
},
{
  id: 3,
  content: "content 1"
}]
function InfiniteScrollList({ items, itemHeight, containerHeight }) {
  const [scrollTop, setScrollTop] = useState(0);

  const totalHeight = items.length * itemHeight;
  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(
    startIndex + Math.ceil(containerHeight / itemHeight),
    items.length - 1
  );

  const visibleItems = items.slice(startIndex, endIndex + 1);
  const offsetTop = startIndex * itemHeight;

  const handleScroll = (event) => {
    setScrollTop(event.currentTarget.scrollTop);
  };

  return (
    <div
      style={{ height: `${containerHeight}px`, overflowY: "scroll" }}
      onScroll={handleScroll}
    >
      <div style={{ height: `${totalHeight}px`, position: "relative" }}>
        <div style={{ position: "absolute", top: `${offsetTop}px` }}>
          {visibleItems.map((item) => (
            <div key={item.id} style={{ height: `${itemHeight}px` }}>
              {item.content}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

export default InfiniteScrollList;

Concluding Thoughts:

Creating a React Virtualized List might seem intricate, but understanding the core concepts and breaking down the process into manageable steps can make it approachable. This excercise has been found in a real senior front-end interview. After you succesfully created the component, you can further expand a lot of concepts to show off your skills, for example: how would you test it? What are and how would you handle edge cases?

For more insightful content and to master frontend interviews, explore our comprehensive resource at FrontSage. Stay tuned for more exercises in this series, and let’s continue our journey towards frontend mastery together!

Keep practicing, stay curious and if you liked this article give a like <3