Lightning Web Components – Events & Communication Questions
Effective communication between components is crucial for building cohesive and maintainable LWC applications. These questions cover the different event communication patterns, custom event handling, parent-child communication, Lightning Message Service, and best practices for implementing robust component interactions. Understanding these concepts will help you build well-structured and scalable LWC solutions.
LWC Events & Communication - Q&A
- Q1. What are the different event communication patterns in LWC?
Ans: Four key patterns: - Parent-to-Child: Using public methods/properties (@api) - Child-to-Parent: Custom events with dispatchEvent() - Sibling Components: Parent as mediator or pub/sub - Unrelated Components: Lightning Message Service or state management - Q2. How do you dispatch and handle custom events?
Ans: Event flow:// Child component (dispatch) this.dispatchEvent(new CustomEvent('notify', { detail: { message: 'Hello' }, bubbles: true, // Propagates up composed: false // Doesn't cross shadow boundary })); // Parent component (handle)
handleNotification(event) { console.log(event.detail.message); } - Q3. What's the difference between bubbles and composed in events?
Ans: Event propagation: - bubbles=true: Event bubbles up DOM hierarchy - composed=true: Event crosses shadow DOM boundaries - Defaults: bubbles=false, composed=false - Use composed carefully (security implications) - Q4. How do you expose child methods to parent components?
Ans: Using @api methods:// Child component @api refreshData() { return fetchData(); } // Parent component
handleCreate(event) { this.childRef = event.detail; this.childRef.refreshData().then(...); } - Q5. How do you implement two-way data binding?
Ans: Pattern combining @api and events:// Child component @api value; handleChange(event) { this.dispatchEvent(new CustomEvent('change', { detail: { value: event.target.value } })); } // Parent component
handleChange(event) { this.boundValue = event.detail.value; } - Q6. What is Lightning Message Service and when to use it?
Ans: Cross-component messaging: - Use when components aren't in same hierarchy - Implementation:// messageChannel.js import { LightningElement, wire } from 'lwc'; import { publish, MessageContext } from 'lightning/messageService'; import SAMPLEMC from '@salesforce/messageChannel/SampleMessageChannel__c'; export default class Publisher extends LightningElement { @wire(MessageContext) messageContext; handlePublish() { const payload = { recordId: '123' }; publish(this.messageContext, SAMPLEMC, payload); } } // Subscriber component @wire(MessageContext) messageContext; subscription; connectedCallback() { this.subscription = subscribe( this.messageContext, SAMPLEMC, (message) => { console.log(message.recordId); } ); }
- Q7. How do you communicate between LWC and Aura components?
Ans: Interoperability methods: - Use aura:method in wrapper Aura component - Application events (Aura) + LMS (LWC) - Custom events with composed:true Example Aura wrapper: - Q8. What are the best practices for event naming?
Ans: Naming conventions: - Use lowercase with hyphens (e.g., 'itemselected') - Prefix with domain (e.g., 'cartupdated') - Avoid generic names ('change', 'click') - Document event signatures - Example good name: 'productquantitychanged' - Q9. How do you test event dispatching?
Ans: Testing strategy:// Test event dispatch const element = createElement('c-publisher', { is: Publisher }); document.body.appendChild(element); // Simulate user action element.shadowRoot.querySelector('button').click(); // Verify event return Promise.resolve().then(() => { const dispatchedEvents = element.getDispatchedEvents(); assert(dispatchedEvents.length === 1); });
- Q10. How do you prevent memory leaks with events?
Ans: Cleanup patterns: - Remove event listeners in disconnectedCallback:connectedCallback() { this.addEventListener('keydown', this.handleKey); } disconnectedCallback() { this.removeEventListener('keydown', this.handleKey); }
- Unsubscribe LMS subscriptions - Clear timeouts/intervals - Q11. How do you implement a pub/sub pattern in LWC?
Ans: Custom event bus:// eventBus.js const callbacks = {}; export const register = (eventName, callback) => { if (!callbacks[eventName]) callbacks[eventName] = new Set(); callbacks[eventName].add(callback); }; export const unregister = (eventName, callback) => { callbacks[eventName]?.delete(callback); }; export const dispatch = (eventName, payload) => { callbacks[eventName]?.forEach(cb => cb(payload)); }; // Usage in components import { register, unregister } from 'c/eventBus'; connectedCallback() { register('dataUpdate', this.handleUpdate.bind(this)); } disconnectedCallback() { unregister('dataUpdate', this.handleUpdate.bind(this)); }
- Q12. How do you handle errors in event communication?
Ans: Error handling patterns: - Include error details in event payloads - Use status flags in events:this.dispatchEvent(new CustomEvent('load', { detail: { data: null, error: 'Failed to load', status: 'error' } }));
- Implement global error event listeners - Q13. How do you communicate between LWCs in different DOM branches?
Ans: Solutions: 1. Lightning Message Service (preferred) 2. Parent mediator (common ancestor) 3. Custom event bus (singleton service) 4. Browser storage (localStorage events) - Q14. How do you implement drag-and-drop events?
Ans: Native event handling:handleDragStart(event) { event.dataTransfer.setData('text/plain', this.itemId); } handleDrop(event) { const itemId = event.dataTransfer.getData('text/plain'); // Handle drop logic }
- Q15. How do you throttle event handlers for performance?
Ans: Rate-limiting pattern:handleScroll = (function() { let lastCall = 0; const delay = 200; // ms return function(event) { const now = new Date().getTime(); if (now - lastCall < delay) return; lastCall = now; // Actual scroll handling }; })();
- Q16. How do you test LMS subscriptions?
Ans: Testing approach:// Create test message context const messageContext = createMessageContext(); // Create and connect component const element = createElement('c-subscriber', { is: Subscriber }); element.messageContext = messageContext; document.body.appendChild(element); // Publish test message publish(messageContext, SAMPLEMC, { recordId: '123' }); // Verify handling return Promise.resolve().then(() => { const consoleSpy = spyOn(console, 'log'); expect(consoleSpy).toHaveBeenCalledWith('123'); });
- Q17. How do you document event contracts between components?
Ans: Documentation practices: - JSDoc for @api methods/events - Markdown docs in component folders - Example event documentation:/** * Fires when data loading completes * @event DataLoader#load * @type {CustomEvent} * @property {Object} detail - Event payload * @property {Array} detail.data - Loaded records * @property {string} detail.status - "success" or "error" */