Simple store using JavaScript patterns
Introduction
This challenge introduces a Simple Store App that demonstrates various JavaScript patterns by simulating product management and cart interactions. The goal is to help developers understand how different patterns handle shared state across multiple components, such as the product list, cart, and header. Each pattern brings its own strengths and weaknesses, and this exercise provides an opportunity to explore when and why certain patterns work best.
By the end of this challenge, you’ll have a deeper understanding of patterns like Event Listener, Observer, Pub/Sub, State Machines, RxJS, Redux, and Signals. You’ll also experience the challenges of state synchronization and component communication—valuable skills in real-world application development.
Description:
Create a Simple Store App where users can:
- Browse products and add them to the cart.
- Remove products from the cart.
- Display the total number of items in the cart within the header.
- Persist the cart state in
localStorage
across page refreshes.
Requirements:
- The product list, cart, and header should be treated as separate components.
- When a user adds or removes items, the cart count in the header should update immediately.
- Cart state must be shared between components (e.g., between product list, cart, and header).
Specific Challenges for Each Pattern / Technique:
1. Event Listener (DOM API):
- Challenge: Use event listeners to manage all user interactions (add/remove actions) and update the cart count manually.
- Goal: Highlight the pain of scaling by trying to keep different components in sync without structured state management.
- Hint: You’ll quickly see how difficult it is to share state between components with only event listeners.
2. Observer Pattern:
- Challenge: Create an observer system where the cart acts as the subject. Observers (e.g., cart UI, header counter) are notified when items are added or removed.
- Goal: Decouple state changes (cart updates) from rendering logic using observers.
- Hint: Ensure both the cart UI and the header counter respond to cart updates.
3. Pub/Sub Pattern:
- Challenge: Use a Pub/Sub system where the product list, cart, and header publish and subscribe to events (e.g., “itemAdded,” “cartUpdated”).
- Goal: Achieve loose coupling so that the cart state and header counter update independently when an event is fired.
4. State Machine:
- Challenge: Use a state machine to manage cart states (e.g., Empty, ItemsInCart, CheckingOut) and transition logic.
- Goal: Enforce valid state transitions (e.g., prevent removing items when the cart is empty).
5. Reactive Programming (RxJS):
- Challenge: Use RxJS streams to manage cart state and sync it with the header and product components.
- Goal: Show how reactive streams propagate cart updates across components (header, product list) in real-time.
6. Redux:
- Challenge: Use Redux to centralize state with actions for adding, removing, and updating the cart. Use middleware to sync the cart with
localStorage
. - Goal: Manage global state predictably across components (cart, header, product list) using a reducer and dispatching actions.
7. Signals (Fine-Grained Reactivity):
- Challenge: Use signals to manage individual pieces of cart state. Components (e.g., header and cart) should observe specific signals.
- Goal: Ensure only the necessary UI parts (like the header counter) update when signals change.
8. Vue (Reactivity System):
- Challenge: Use Vue’s reactivity system with
ref()
orreactive()
to manage cart state and keep components (product list, cart, header) in sync. - Goal: Leverage Vue’s built-in reactivity to automatically update the cart and header when state changes, without manually managing event listeners.
Summary:
This Simple Store App forces developers to share state across components, a common challenge in web development. Here’s a recap:
- Event Listener: Directly handle events but struggle with scaling and state syncing.
- Observer: Decouple state updates from rendering logic by notifying multiple observers.
- Pub/Sub: Loosely connect components by publishing and subscribing to events.
- State Machine: Enforce structured transitions between cart states.
- RxJS: Use streams to propagate updates asynchronously across components.
- Redux: Centralize state and manage updates predictably across the app.
- Signals: Achieve fine-grained reactivity with minimal overhead.
This challenge is small enough to be approachable but realistic enough to demonstrate the strengths and weaknesses of each technique. It would be a preeetttty good project for a portfolio of studies and something unique to talk about during interviews.