Reactive Action Subscribers in Vue with TypeScript
This challenge focuses on building a reusable component in Vue.js that allows subscribers to react to actions emitted from a central action bus. This pattern is useful for decoupling components and enabling communication between them without direct parent-child relationships, promoting a more maintainable and scalable application architecture. You'll be implementing a subscriber component that registers for specific actions and executes provided callbacks when those actions are triggered.
Problem Description
You need to create a Vue component called ActionSubscriber that subscribes to an action bus (represented as an Emitters object – see Notes for details) and executes a callback function when a specific action is emitted. The component should accept the following props:
action: (string) The name of the action to subscribe to.callback: (Function) The function to execute when the specified action is emitted. This function should accept a single argument: the payload associated with the action.bus: (Emitters) The action bus to subscribe to.
The component should automatically subscribe to the specified action when mounted and unsubscribe when unmounted. The callback function should be executed with the payload of the emitted action.
Key Requirements:
- The component must subscribe to the action bus using the provided
actionname. - The component must execute the provided
callbackfunction with the action's payload when the action is emitted. - The component must unsubscribe from the action bus when unmounted to prevent memory leaks.
- The component should handle cases where the
actionprop is invalid (e.g., empty string or null). - The component should handle cases where the
callbackprop is not a function.
Expected Behavior:
When the component is mounted, it should subscribe to the specified action. When the action is emitted, the callback function should be executed with the payload. When the component is unmounted, it should unsubscribe from the action. If the action prop is invalid, the component should not subscribe. If the callback prop is not a function, it should log an error to the console and not subscribe.
Edge Cases to Consider:
- What happens if the
actionprop changes dynamically? Should the component unsubscribe from the old action and subscribe to the new one? (For this challenge, assume the action is static after mounting.) - What happens if the
callbackprop changes dynamically? (For this challenge, assume the callback is static after mounting.) - What happens if the action bus is not provided?
- What happens if the action bus emits an action with no payload?
Examples
Example 1:
Input:
action = "userLoggedIn",
callback = (payload: any) => console.log("User logged in:", payload),
bus = { emit: (action: string, payload: any) => console.log(`Action ${action} emitted with payload: ${JSON.stringify(payload)}`) }
bus.emit("userLoggedIn", { userId: 123, username: "testuser" });
Output:
Action userLoggedIn emitted with payload: {"userId":123,"username":"testuser"}
User logged in: { userId: 123, username: 'testuser' }
Explanation: The component subscribes to "userLoggedIn". When the action is emitted, the callback function is executed with the payload.
Example 2:
Input:
action = null,
callback = (payload: any) => console.log("Callback executed", payload),
bus = { emit: (action: string, payload: any) => console.log(`Action ${action} emitted with payload: ${JSON.stringify(payload)}`) }
Output:
(No subscription occurs, no console log from bus.emit)
Explanation: The component does not subscribe because the action is null.
Example 3:
Input:
action = "dataLoaded",
callback = "not a function",
bus = { emit: (action: string, payload: any) => console.log(`Action ${action} emitted with payload: ${JSON.stringify(payload)}`) }
Output:
Error: Callback must be a function.
(No subscription occurs, no console log from bus.emit)
Explanation: The component logs an error because the callback is not a function and does not subscribe.
Constraints
- The component must be written in Vue 3 with TypeScript.
- The
Emittersobject should have anemitmethod that accepts an action name (string) and a payload (any). - The component should be reusable and not tightly coupled to any specific application logic.
- The component should handle errors gracefully and prevent crashes.
- The component should not introduce any unnecessary dependencies.
Notes
For the purpose of this challenge, you can assume the existence of an Emitters interface:
interface Emitters {
emit(action: string, payload: any): void;
}
You can create a simple mock implementation of Emitters for testing purposes. Focus on the component's logic and interaction with the action bus. Consider using onBeforeMount and onUnmounted lifecycle hooks for subscribing and unsubscribing. Error handling is important – ensure the component behaves predictably when invalid props are provided.