BRUCE_FEBRUCE_FE

EN/CH Mode

BRUCE_FE JS Interview Notes - Implement Data Fetching and UI Updates

Learn how to efficiently implement frontend data fetching and UI update workflows, master asynchronous data processing, loading state management, error handling, and optimized rendering best practices.

影片縮圖

Lazy to read articles? Then watch videos!

Data Fetching and UI Updates Overview

In frontend development, fetching data from servers and updating user interfaces is a basic but critical task. This process involves asynchronous request handling, loading state management, error handling, and efficient UI update strategies. Well-designed data fetching and UI update workflows are the foundation for building good user experiences.

Main Challenges and Considerations

  • 1. Asynchronous processing: Correctly handle the asynchronous nature of network requests
  • 2. Loading state: Provide appropriate visual feedback during data loading
  • 3. Error handling: Gracefully handle potential network or data errors
  • 4. Data transformation: Convert API responses to formats suitable for UI presentation
  • 5. Optimized rendering: Avoid unnecessary renders to improve performance
  • 6. Data caching: Implement appropriate caching strategies to reduce duplicate requests

Basic Data Fetching and UI Update Implementation

Below is a basic data fetching and UI update example using vanilla JavaScript and DOM API:

// Basic data fetching and UI update implementation
document.addEventListener('DOMContentLoaded', function() {
  const userListElement = document.getElementById('user-list');
  const loadingElement = document.getElementById('loading');
  const errorElement = document.getElementById('error');
  
  // Show loading state
  function showLoading() {
    loadingElement.style.display = 'block';
    errorElement.style.display = 'none';
    userListElement.innerHTML = '';
  }
  
  // Show error message
  function showError(message) {
    loadingElement.style.display = 'none';
    errorElement.style.display = 'block';
    errorElement.textContent = message || 'An error occurred, please try again later';
    userListElement.innerHTML = '';
  }
  
  // Display data
  function renderUsers(users) {
    loadingElement.style.display = 'none';
    errorElement.style.display = 'none';
    userListElement.innerHTML = '';
    
    if (users.length === 0) {
      userListElement.innerHTML = '<p>No users found</p>';
      return;
    }
    
    const userElements = users.map(user => {
      return `
        <div class="user-card">
          <h3>${user.name}</h3>
          <p>Email: ${user.email}</p>
        </div>
      `;
    });
    
    userListElement.innerHTML = userElements.join('');
  }
  
  // Fetch user data
  async function fetchUsers() {
    try {
      showLoading();
      
      const response = await fetch('https://jsonplaceholder.typicode.com/users');
      
      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`);
      }
      
      const users = await response.json();
      renderUsers(users);
    } catch (error) {
      console.error('Failed to fetch user data:', error);
      showError('Unable to load user data: ' + error.message);
    }
  }
  
  // Add retry button event handler
  document.getElementById('retry-button').addEventListener('click', fetchUsers);
  
  // Initial data fetch
  fetchUsers();
});

The corresponding HTML structure is as follows:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>User List</title>
  <style>
    .user-card {
      border: 1px solid #ccc;
      padding: 15px;
      margin-bottom: 10px;
      border-radius: 4px;
    }
    #loading, #error {
      display: none;
      padding: 15px;
    }
    #error {
      color: red;
    }
  </style>
</head>
<body>
  <h1>User List</h1>
  
  <div id="loading">Loading, please wait...</div>
  <div id="error"></div>
  <button id="retry-button">Retry</button>
  
  <div id="user-list"></div>
  
  <script src="app.js"></script>
</body>
</html>

React Virtual List Simple Implementation

Below is a concise React virtual list implementation that only renders elements within the visible area:

import React, { useState, useEffect, useRef } from 'react';

const VirtualList = ({ items, itemHeight, containerHeight = 400 }) => {
  // State to track scroll position
  const [scrollTop, setScrollTop] = useState(0);
  // Reference to container DOM element
  const containerRef = useRef(null);
  // Flag to prevent excessive rendering
  const ticking = useRef(false);

  // Calculate visible item count (render 2 extra items as buffer)
  const visibleCount = Math.ceil(containerHeight / itemHeight) + 2;
  // Calculate start index (subtract 1 to pre-render one item)
  const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - 1);
  // Calculate end index (ensure it doesn't exceed array bounds)
  const endIndex = Math.min(items.length - 1, startIndex + visibleCount);

  // Scroll event handler, use requestAnimationFrame to optimize performance
  const onScroll = (e) => {
    const newTop = e.target.scrollTop;
    if (!ticking.current) {
      requestAnimationFrame(() => {
        setScrollTop(newTop);
        ticking.current = false;
      });
      ticking.current = true;
    }
  };

  return (
    <div
      ref={containerRef}
      onScroll={onScroll}
      style={{ height: containerHeight, overflow: 'auto', position: 'relative' }}
    >
      {/* Create a container with height equal to total height of all items */}
      <div style={{ height: items.length * itemHeight }}>
        {/* Only render items within visible range */}
        {items.slice(startIndex, endIndex + 1).map((item, i) => {
          const index = startIndex + i;
          return (
            <div
              key={index}
              style={{
                position: 'absolute', // Use absolute positioning
                top: index * itemHeight, // Calculate correct position based on index
                height: itemHeight,
                width: '100%',
              }}
            >
              {item}
            </div>
          );
        })}
      </div>
    </div>
  );
};

// Usage example
const App = () => {
  // Generate 1000 test items
  const items = Array.from({ length: 1000 }, (_, i) => (
    <div style={{ padding: '10px', borderBottom: '1px solid #eee' }}>
      Item #{i + 1}
    </div>
  ));
  
  return (
    <div>
      <h2>Virtual List Example</h2>
      <VirtualList
        items={items}
        itemHeight={50}
        containerHeight={300}
      />
    </div>
  );
};

Core principles:

  • 1. Only render items in the visible area
  • 2. Use absolute positioning to place elements
  • 3. Container height is fixed, content height equals total height of all items
  • 4. Dynamically calculate displayed items based on scroll position