DeployU
Interviews / Frontend Engineering / This component manages loading state imperatively. Refactor it to use React Suspense.

Questions

This component manages loading state imperatively. Refactor it to use React Suspense.

refactoring Component Patterns Interactive Quiz Code Examples

A developer has a UserProfile component that fetches user data from an API. Currently, they manage the loading state manually using useState and useEffect, displaying a “Loading…” message while waiting for the data.

They want to improve the user experience and simplify the code by using React Suspense to declaratively handle the loading state.

You’ve been given the UserProfile component and a mock API.

  1. Refactor the application to use React.Suspense to declaratively handle the loading state for the UserProfile component.
  2. The UserProfile component should “suspend” its rendering until the data is available.
  3. The App component should display a fallback UI while UserProfile is waiting for data.
Your Code
import React, { useState, useEffect } from 'react';

// Mock API call that simulates fetching user data
const fetchUserData = () => {
  console.log('Fetching user data...');
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ id: 1, name: 'Alice', email: 'alice@example.com' });
    }, 2000); // Simulate network delay
  });
};

// UserProfile component with imperative loading state management
function UserProfile() {
  const [userData, setUserData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    fetchUserData().then(data => {
      setUserData(data);
      setLoading(false);
    });
  }, []);

  if (loading) {
    return <div>Loading user profile...</div>;
  }

  return (
    <div style={{ border: '1px solid #ccc', padding: '20px', margin: '10px' }}>
      <h2>User Profile</h2>
      <p>ID: {userData.id}</p>
      <p>Name: {userData.name}</p>
      <p>Email: {userData.email}</p>
    </div>
  );
}

export default function App() {
  return (
    <div style={{ padding: '20px' }}>
      <h1>Application</h1>
      <UserProfile />
    </div>
  );
}
Click "Run Code" to see output...
Click "Run Code" to see test results...
How Different Experience Levels Approach This
Junior Engineer
Surface Level

import React, { useState, useEffect } from 'react'; // Mock API call that simulates fetching user data const fetchUserData = () => { console.log('Fetching user data...'); return new Promise(resolve => { setTimeout(() => { resolve({ id: 1, name: 'Alice', email: 'alice@example.com' }); }, 2000); // Simulate network delay }); }; // UserProfile component with imperative loading state management function UserProfile() { const [userData, setUserData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { setLoading(true); fetchUserData().then(data => { setUserData(data); setLoading(false); }); }, []); if (loading) { return <div>Loading user profile...</div>; } return ( <div style={{ border: '1px solid #ccc', padding: '20px', margin: '10px' }}> <h2>User Profile</h2> <p>ID: {userData.id}</p> <p>Name: {userData.name}</p> <p>Email: {userData.email}</p> </div> ); } export default function App() { return ( <div style={{ padding: '20px' }}> <h1>Application</h1> <UserProfile /> </div> ); }

Senior Engineer
Production Ready

import React, { useState, useEffect, Suspense } from 'react'; // 1. Create a Suspense-compatible resource utility // This is a simplified example. In a real app, you'd use a library like // React Query, SWR, or Relay, which provide Suspense integration. function createResource(promise) { let status = 'pending'; let result; let suspender = promise.then( r => { status = 'success'; result = r; }, e => { status = 'error'; result = e; } ); return { read() { if (status === 'pending') { throw suspender; // Suspense catches this promise } else if (status === 'error') { throw result; } else if (status === 'success') { return result; } } }; } // Mock API call that simulates fetching user data const fetchUserData = () => { console.log('Fetching user data...'); return new Promise(resolve => { setTimeout(() => { resolve({ id: 1, name: 'Alice', email: 'alice@example.com' }); }, 2000); // Simulate network delay }); }; // Create the resource once outside the component const userResource = createResource(fetchUserData()); // UserProfile component now uses the resource directly function UserProfile() { // 2. Read data from the resource. This will suspend if data is not ready. const userData = userResource.read(); console.log('UserProfile rendered with data:', userData); return ( <div style={{ border: '1px solid #ccc', padding: '20px', margin: '10px' }}> <h2>User Profile</h2> <p>ID: {userData.id}</p> <p>Name: {userData.name}</p> <p>Email: {userData.email}</p> </div> ); } export default function App() { return ( <div style={{ padding: '20px' }}> <h1>Application</h1> {/* 3. Wrap UserProfile with Suspense and provide a fallback */} <Suspense fallback={<div>Loading data with Suspense...</div>}> <UserProfile /> </Suspense> </div> ); }

What Makes the Difference?
  • Context over facts: Explains when and why, not just what
  • Real examples: Provides specific use cases from production experience
  • Trade-offs: Acknowledges pros, cons, and decision factors

Practice Question

What is the primary purpose of React.Suspense?