DEV Community

Olayemi Elsie
Olayemi Elsie

Posted on • Edited on

Vue 3 Task Manager App: Problem-Solving Approach

Developing a Task Manager application using Vue 3 focused on:

  • Component composition
  • Props and events
  • State management
  • Composables
  • Clean structure and separation of concerns

What the Task Manager App Does

The Task Manager allows users to:

  1. Add tasks
  2. Mark tasks as completed
  3. Delete tasks
  4. Toggle all tasks (Check All / Uncheck All)
  5. Delete all tasks
  6. Switch between dark and light mode
  7. Edit tasks inline

PLANNING PHASE

Before writing code, I thought about:

  • Where should the task state live?
  • How will components communicate?
  • Should I use local state or a shared composable?

Breaking Down the Problem

I split the app into smaller parts:

  • TaskManager (parent)
  • CardViewComponent
  • CheckAllBtns
  • EmptyState
  • useTasks (composable)

This made the app easier to understand and maintain, though I didn't
plan all the UX requirements at first.

KEY DESIGN DECISIONS

  1. Composable for State Management

    • Problem: Multiple components needed access to task data
    • Decision: Created useTasks composable
    • Reason: Centralizes task data and most operations
  2. Separate Components

    • Problem: App growing too large
    • Decision: Split into focused components
    • Reason: Easier to read, maintain, and debug
  3. Slots in EmptyState

    • Problem: Need flexible empty state content
    • Decision: Use slots instead of props
    • Reason: Slots pass content (HTML), props pass data
  4. CSS Classes for Theming (Not Dynamic Components)

    • Initial mistake: Created both CardView and ListView thinking I needed separate components for light and dark modes
    • Problem: Both components had identical code only CSS colors differed
    • Realization: Dark/light mode is a styling concern, not a component structure concern
    • Decision: Deleted ListView, used CSS class binding instead: :class="isDark ? 'dark' : 'light'"
    • Reason: One component with dynamic CSS classes is cleaner than duplicating components for themes
    • Lesson: Dynamic components are for structurally different layouts (grid vs list), not for styling variations (light vs dark)

CHALLENGES & SOLUTIONS

Challenge 1: Misunderstanding Dynamic Components and Duplicate State

  • What went wrong: Initially thought I needed separate components (CardView and ListView) for light and dark themes. While building ListView, I created a local tasks array in both files
  • Problems discovered:
    1. Both components had identical structure only CSS differed (wrong use of dynamic components)
    2. ListView's local tasks array didn't sync with CardView's composable based tasks (duplicate state)
  • Why sync failed: CardView and ListView were using two separate task arrays in different locations, not one shared source
  • Fix:
    1. Realized theming is a CSS concern, not a component concern
    2. Deleted ListView entirely
    3. Implemented theming with CSS class binding: :class="isDark ? 'dark' : 'light'"
  • Lessons learned:
    • Dynamic components = Different structures/layouts (e.g., grid vs list)
    • CSS classes = Different styles/themes (e.g., light vs dark)
    • Multiple views must share the SAME data source from composable, not create separate copies
    • Don't duplicate components when CSS can solve the problem

Challenge 2: deleteAllTasks location

  • What went wrong: Placed in child component initially
  • Problem: Child shouldn't manage global state
  • Fix: Moved to parent component, triggered via events
  • Current state: Still in parent (TaskManager.vue)
  • Lesson: Child emits events; parent/composable manages state

Challenge 3: Missing UX requirements

  • What went wrong: Didn't plan for:
    • Text overflow (long task names)
    • Empty state display
    • Visual feedback for completed tasks
  • Fix:
    • Added truncateText() function (max 30 characters)
    • Created EmptyState component
    • Added linethrough styling for completed tasks
  • Lesson: Plan UX requirements well before implementation, not just functionality

Challenge 4: Slots vs Props

  • What went wrong: Tried passing empty state content via props
  • Problem: Props only handle simple data (strings, numbers), not HTML or components
  • Fix: Used slots for flexible content can now pass any HTML, icons, or components
  • Lesson: Props = data, Slots = content

KEY TAKEAWAYS

  1. Component vs CSS: Don't create separate components for styling differences use CSS classes for themes. Dynamic components should only be used when components are structurally different.

  2. Single Source of Truth: Multiple views must share the SAME data source (composable), not create duplicate state. Changes to shared data should reflect everywhere immediately.

  3. Architectural Consistency: All operations on shared state should live in one place (composable) for consistency and maintainability.

  4. UX Planning: Important UX requirements (empty states, text handling, visual feedback) should be considered during planning, not added during execution.

  5. Understanding Tools:

    • Dynamic components = Different structure/functionality (CardView grid layout vs ListView vertical layout)
    • CSS classes = Different appearance/theme (light mode vs dark mode)
    • Using the wrong tool leads to code duplication and complexity

Planning and breaking the app into components made implementation easier. Recognizing when I was overcomplicating things (separate components for themes) and understanding the right tool for each job (CSS vs dynamic components) were important lessons in building maintainable Vue applications.

Top comments (0)