DeployU
Interviews / Frontend Engineering / This legacy class component mixes logic and UI. Refactor it to a modern functional component using Hooks.

Questions

This legacy class component mixes logic and UI. Refactor it to a modern functional component using Hooks.

practical Refactoring Interactive Quiz Code Examples

You’ve been tasked with modernizing a part of a legacy React codebase. You find an old class component, UserListContainer, that fetches a list of users and renders them.

This component mixes data fetching, state management, and rendering logic all in one place. This makes the UI difficult to reuse or test in isolation. It follows the old “Container Component” pattern, but it’s time for an update.

Refactor this legacy component. Your goal is to separate the logic from the presentation using modern React Hooks.

  1. Create a custom Hook called useUsers to encapsulate the logic for fetching and managing the user data.
  2. Create a purely presentational component called UserList that receives the users and loading state as props and is only responsible for rendering the UI.
  3. Create a new UserPage component that uses your useUsers hook and your UserList component to render the final output.

The final UI should look and function identically to the original.

Refactor Class Component to Hooks
Your Code
import React, { Component } from 'react';

// Mock API call
const fetchUsers = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve([
        { id: 1, name: 'Alice' },
        { id: 2, name: 'Bob' },
        { id: 3, name: 'Charlie' },
      ]);
    }, 1000);
  });
};

// The legacy class component that mixes concerns
export default class UserListContainer extends Component {
  state = {
    users: [],
    loading: true,
  };

  componentDidMount() {
    fetchUsers().then(users => {
      this.setState({ users, loading: false });
    });
  }

  render() {
    const { users, loading } = this.state;

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

    return (
      <div>
        <h2>Users</h2>
        <ul>
          {users.map(user => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      </div>
    );
  }
}
// Validate the refactor
const code = document.querySelector('[data-editor]').textContent;

// Check for custom hook
if (!code.includes('useUsers') || !code.includes('const useUsers =') && !code.includes('function useUsers')) {
  throw new Error('Test failed: Create a custom hook called useUsers to encapsulate the data fetching logic.');
}

// Check that hook uses useState
if (!code.match(/useUsers[\s\S]*?useState/)) {
  throw new Error('Test failed: The useUsers hook should use useState to manage users and loading state.');
}

// Check that hook uses useEffect
if (!code.match(/useUsers[\s\S]*?useEffect/)) {
  throw new Error('Test failed: The useUsers hook should use useEffect to fetch data on mount.');
}

// Check for presentational component
if (!code.includes('UserList')) {
  throw new Error('Test failed: Create a UserList presentational component that receives users and loading as props.');
}

// Check that no class component
if (code.includes('extends Component') || code.includes('extends React.Component')) {
  throw new Error('Test failed: Refactor from class component to functional components with hooks.');
}

// Success!
console.log('✓ All tests passed! Successfully refactored to modern React patterns.');
console.log('✓ Logic is separated from presentation using custom hooks.');
Click "Run Code" to see output...
Click "Run Code" to see test results...
How Different Experience Levels Approach This
Junior Engineer
Surface Level

I would convert the class component to a functional component and move all the code into the function body, using useState and useEffect for the state and lifecycle methods.

Senior Engineer
Production Ready

I would extract the stateful logic into a reusable custom hook called `useUsers` that encapsulates the data fetching with useState and useEffect. Then, I'd create a pure presentational component `UserList` that only handles rendering. Finally, a `UserPage` component would compose these together, achieving proper separation of concerns and making both the logic and UI independently reusable and testable.

The original code uses the Container and Presentational Component Pattern. The UserListContainer is a “smart” container that knows how to fetch data and manage state. It then renders the UI directly. The problem is that the UI (the ul and li elements) is not reusable without the data-fetching logic.

With the introduction of React Hooks, this pattern has evolved. We can achieve a much cleaner separation of concerns by extracting all the stateful logic into a custom Hook.

  1. useUsers Hook: This hook is our new “container.” It handles the useState for users and loading, and the useEffect for fetching the data. It returns the stateful values. This logic is now reusable by any component.
  2. UserList Component: This becomes our “dumb” presentational component. It receives users and loading as props and does nothing but render the UI. It’s now easy to test and reuse.
  3. UserPage Component: This component brings the two together. It calls the useUsers hook to get the data and then passes that data to the UserList component to be rendered.

This approach is more flexible, composable, and aligns with modern React best practices.

Here is the corrected implementation:

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

const fetchUsers = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve([
        { id: 1, name: 'Alice' },
        { id: 2, name: 'Bob' },
        { id: 3, name: 'Charlie' },
      ]);
    }, 1000);
  });
};

// 1. The custom Hook for logic
const useUsers = () => {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUsers().then(fetchedUsers => {
      setUsers(fetchedUsers);
      setLoading(false);
    });
  }, []); // Runs once on mount

  return { users, loading };
};

// 2. The dumb presentational component
const UserList = ({ users, loading }) => {
  if (loading) {
    return <div>Loading users...</div>;
  }

  return (
    <div>
      <h2>Users</h2>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

// 3. The final component bringing them together
export default function UserPage() {
  const { users, loading } = useUsers();
  return <UserList users={users} loading={loading} />;
}
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 benefit of separating data logic from UI presentation in a React application?