Hone logo
Hone
Problems

Implementing a Custom Store Subscription Mechanism in Vue

This challenge focuses on building a core piece of reactive state management in Vue: the subscription mechanism. You'll implement a way for components to react to changes in a central store, enabling dynamic updates and a more robust application architecture. This is fundamental to how many popular state management libraries like Pinia and Vuex operate.

Problem Description

Your task is to create a simplified store with a subscribe method that allows external code (typically Vue components) to register callback functions. These callbacks should be invoked whenever the store's state changes.

What needs to be achieved:

You need to design and implement a Store class in TypeScript that manages a piece of state and provides a mechanism to subscribe to its changes.

Key requirements:

  1. State Management: The Store should hold a piece of state.
  2. State Mutation: A method (e.g., setState or mutate) should exist to update the state.
  3. Subscription: A subscribe method should allow registering callback functions.
  4. Callback Invocation: When the state is mutated, all registered subscribers should be called.
  5. Unsubscription: A mechanism to unsubscribe from future state changes is crucial.

Expected behavior:

  • When subscribe is called, the provided callback function is added to a list of active subscribers.
  • When the state is updated using the mutation method, each function in the list of subscribers is executed.
  • When an unsubscribe function (returned by subscribe) is called, the corresponding callback is removed from the subscriber list, and it will no longer be invoked for future state changes.

Edge cases to consider:

  • Subscribing multiple times with the same callback.
  • Unsubscribing multiple times.
  • Mutating the state when there are no subscribers.
  • What happens if a subscriber function throws an error? (For this challenge, we assume subscribers will not throw errors to keep the scope manageable).

Examples

Let's illustrate with a conceptual example.

Example 1: Basic Subscription and Mutation

// Assume a simplified Store class exists with subscribe and setState methods

// Initial state
const store = new Store({ count: 0 });

// Callback function to be notified of changes
const subscriber = (newState: { count: number }) => {
  console.log('State changed:', newState);
};

// Subscribe to state changes
const unsubscribe = store.subscribe(subscriber);

// Mutate the state
store.setState({ count: 1 });
// Expected Console Output:
// State changed: { count: 1 }

// Mutate the state again
store.setState({ count: 2 });
// Expected Console Output:
// State changed: { count: 2 }

Example 2: Unsubscribing

// Assume a simplified Store class exists with subscribe and setState methods

const store = new Store({ user: 'Guest' });

const subscriber1 = (newState: { user: string }) => {
  console.log('Subscriber 1 notified:', newState);
};

const subscriber2 = (newState: { user: string }) => {
  console.log('Subscriber 2 notified:', newState);
};

const unsubscribe1 = store.subscribe(subscriber1);
store.subscribe(subscriber2); // No need to store unsubscribe for this example

store.setState({ user: 'Alice' });
// Expected Console Output:
// Subscriber 1 notified: { user: 'Alice' }
// Subscriber 2 notified: { user: 'Alice' }

unsubscribe1(); // Unsubscribe subscriber1

store.setState({ user: 'Bob' });
// Expected Console Output:
// Subscriber 2 notified: { user: 'Bob' }
// (Subscriber 1 is no longer called)

Constraints

  • The Store class should be implemented using TypeScript.
  • The state can be of any generic type.
  • The subscribe method should return a function that, when called, removes the subscriber.
  • Efficiency is a secondary concern for this challenge; correctness and clarity of implementation are paramount.

Notes

  • Think about how you will store the list of subscribers. An array or a Set might be suitable.
  • Consider the type safety provided by TypeScript. Use generics to make your Store flexible.
  • The setState method should be responsible for triggering the notifications.
  • Your implementation of setState should likely take the entire new state object, not just partial updates, for simplicity in this challenge.
Loading editor...
typescript