Hone logo
Hone
Problems

Refactoring a Vue Component for Improved Readability and Reusability

This challenge focuses on refactoring a moderately complex Vue component to improve its readability, maintainability, and reusability. You'll be given a component with intertwined logic and styling, and your task is to restructure it using best practices like component composition, computed properties, and potentially extracting smaller, reusable components. This is a common task in real-world Vue development, and mastering it is crucial for building scalable applications.

Problem Description

You are given a Vue component that displays a list of products, allowing users to filter them by category and sort them by price. The component currently has all its logic and styling tightly coupled within a single file. Your task is to refactor this component to adhere to the following principles:

  1. Separate Concerns: Extract the filtering and sorting logic into computed properties. This will make the component's template cleaner and easier to understand.
  2. Component Composition (Optional but Recommended): If possible, identify parts of the component that could be extracted into smaller, reusable components (e.g., a filter dropdown, a sorting control, a product item). This will improve code reusability and testability.
  3. Improved Readability: Use meaningful variable names and comments to enhance the code's clarity.
  4. Maintainability: Structure the code in a way that makes it easy to modify and extend in the future.

Expected Behavior:

The refactored component should maintain the same functionality as the original:

  • Display a list of products.
  • Allow users to filter products by category.
  • Allow users to sort products by price (ascending or descending).
  • The displayed list should update dynamically based on the selected filter and sort order.

Edge Cases to Consider:

  • Empty product list: Handle the case where there are no products to display gracefully.
  • Invalid filter/sort options: Ensure the component handles invalid user input without errors.
  • Performance: While not a primary focus, consider the performance implications of your refactoring, especially if dealing with a large product list.

Examples

Example 1:

Input:
products = [
  { id: 1, name: 'Laptop', category: 'Electronics', price: 1200 },
  { id: 2, name: 'T-Shirt', category: 'Clothing', price: 25 },
  { id: 3, name: 'Headphones', category: 'Electronics', price: 150 },
  { id: 4, name: 'Jeans', category: 'Clothing', price: 75 }
],
filterCategory = 'Electronics',
sortOrder = 'asc'

Output:
[
  { id: 3, name: 'Headphones', category: 'Electronics', price: 150 },
  { id: 1, name: 'Laptop', category: 'Electronics', price: 1200 }
]
Explanation: The products are filtered by 'Electronics' and sorted in ascending order by price.

Example 2:

Input:
products = [
  { id: 1, name: 'Laptop', category: 'Electronics', price: 1200 },
  { id: 2, name: 'T-Shirt', category: 'Clothing', price: 25 },
  { id: 3, name: 'Headphones', category: 'Electronics', price: 150 },
  { id: 4, name: 'Jeans', category: 'Clothing', price: 75 }
],
filterCategory = 'Clothing',
sortOrder = 'desc'

Output:
[
  { id: 4, name: 'Jeans', category: 'Clothing', price: 75 },
  { id: 2, name: 'T-Shirt', category: 'Clothing', price: 25 }
]
Explanation: The products are filtered by 'Clothing' and sorted in descending order by price.

Example 3: (Empty Product List)

Input:
products = [],
filterCategory = 'Electronics',
sortOrder = 'asc'

Output:
[]
Explanation: An empty array is returned as there are no products to display.

Constraints

  • Language: TypeScript
  • Framework: Vue 3 (Composition API preferred, but Options API is acceptable)
  • Data Size: The products array can contain up to 100 items.
  • Performance: The filtering and sorting operations should complete within a reasonable timeframe (e.g., less than 500ms). While optimization isn't the primary goal, avoid excessively inefficient algorithms.
  • Input Format: products is an array of objects, each with id (number), name (string), category (string), and price (number) properties. filterCategory is a string representing the category to filter by. sortOrder is a string, either 'asc' or 'desc'.

Notes

  • You are provided with the initial component code (see below). Your task is to refactor it.
  • Focus on improving the structure and readability of the code.
  • Consider using Vue's reactivity system to ensure the component updates efficiently.
  • Feel free to add any necessary helper functions or utility methods.
  • The provided initial component code is intentionally verbose and lacks proper structure to highlight the benefits of refactoring.

Initial Component Code (to be refactored):

<template>
  <div>
    <h2>Product List</h2>
    <select v-model="filterCategory">
      <option value="">All Categories</option>
      <option value="Electronics">Electronics</option>
      <option value="Clothing">Clothing</option>
    </select>
    <select v-model="sortOrder">
      <option value="asc">Price (Ascending)</option>
      <option value="desc">Price (Descending)</option>
    </select>
    <ul>
      <li v-for="product in products" :key="product.id">
        {{ product.name }} - ${{ product.price }} - {{ product.category }}
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, computed } from 'vue';

export default defineComponent({
  name: 'ProductList',
  setup() {
    const products = ref([
      { id: 1, name: 'Laptop', category: 'Electronics', price: 1200 },
      { id: 2, name: 'T-Shirt', category: 'Clothing', price: 25 },
      { id: 3, name: 'Headphones', category: 'Electronics', price: 150 },
      { id: 4, name: 'Jeans', category: 'Clothing', price: 75 }
    ]);
    const filterCategory = ref('');
    const sortOrder = ref('asc');

    const filteredProducts = computed(() => {
      let filtered = products.value;
      if (filterCategory.value !== '') {
        filtered = filtered.filter(product => product.category === filterCategory.value);
      }
      return filtered;
    });

    const sortedProducts = computed(() => {
      let sorted = [...filteredProducts.value]; // Create a copy to avoid mutating the original
      sorted.sort((a, b) => {
        if (sortOrder.value === 'asc') {
          return a.price - b.price;
        } else {
          return b.price - a.price;
        }
      });
      return sorted;
    });

    return {
      products,
      filterCategory,
      sortOrder,
      filteredProducts,
      sortedProducts
    };
  }
});
</script>
Loading editor...
typescript