Lightning Web Components – Real-Time LWC Scenarios Questions
Real-world LWC development requires solving complex problems and implementing advanced patterns that go beyond basic component creation. These questions cover performance optimization, infinite scrolling, real-time data updates, complex form handling, dynamic component creation, and integration with external systems. Understanding these scenarios will help you tackle challenging requirements and build enterprise-grade LWC applications.
Real-Time LWC Scenarios - Q&A
- Q1. How would you implement a high-performance data table with 10,000+ records?
Ans: Virtual scrolling pattern:import { LightningElement, api } from 'lwc'; export default class VirtualDataTable extends LightningElement { @api records = []; visibleRecords = []; rowHeight = 40; visibleRowCount = 15; scrollTop = 0; get totalHeight() { return this.records.length * this.rowHeight; } get visibleStartIndex() { return Math.floor(this.scrollTop / this.rowHeight); } get visibleEndIndex() { return Math.min( this.visibleStartIndex + this.visibleRowCount, this.records.length ); } get offsetY() { return this.visibleStartIndex * this.rowHeight; } renderedCallback() { this.updateVisibleRecords(); } handleScroll(event) { this.scrollTop = event.target.scrollTop; this.updateVisibleRecords(); } updateVisibleRecords() { this.visibleRecords = this.records.slice( this.visibleStartIndex, this.visibleEndIndex ); } }
- Q2. How would you implement infinite scrolling in LWC?
Ans: Infinite scroll implementation:import { LightningElement, track } from 'lwc'; import getRecords from '@salesforce/apex/RecordController.getRecords'; export default class InfiniteScroll extends LightningElement { @track records = []; isLoading = false; currentPage = 1; pageSize = 20; hasMore = true; connectedCallback() { this.loadRecords(); this.setupScrollListener(); } setupScrollListener() { const container = this.template.querySelector('.container'); container.addEventListener('scroll', this.handleScroll.bind(this)); } async handleScroll(event) { const { scrollTop, scrollHeight, clientHeight } = event.target; // Check if user scrolled to bottom if (scrollTop + clientHeight >= scrollHeight - 5 && !this.isLoading && this.hasMore) { this.loadMoreRecords(); } } async loadRecords() { try { this.isLoading = true; const newRecords = await getRecords({ page: this.currentPage, pageSize: this.pageSize }); this.records = [...this.records, ...newRecords]; this.hasMore = newRecords.length === this.pageSize; this.currentPage++; } catch (error) { console.error('Error loading records:', error); } finally { this.isLoading = false; } } loadMoreRecords() { this.loadRecords(); } }
- Q3. How would you implement real-time data updates using Platform Events?
Ans: Real-time updates with Platform Events:import { LightningElement, track } from 'lwc'; import { subscribe, unsubscribe, onError } from 'lightning/empApi'; import { ShowToastEvent } from 'lightning/platformShowToastEvent'; export default class RealTimeUpdates extends LightningElement { @track records = []; subscription = {}; channelName = '/event/RecordUpdateEvent__e'; connectedCallback() { this.handleSubscribe(); } handleSubscribe() { const messageCallback = (response) => { const payload = response.data.payload; this.updateRecordInList(payload); // Show toast notification this.dispatchEvent( new ShowToastEvent({ title: 'Record Updated', message: `Record ${payload.RecordId__c} was updated`, variant: 'info' }) ); }; subscribe(this.channelName, -1, messageCallback).then((response) => { this.subscription = response; }); } updateRecordInList(payload) { // Update the record in the list this.records = this.records.map(record => { if (record.Id === payload.RecordId__c) { return { ...record, ...payload.Fields__c }; } return record; }); } disconnectedCallback() { unsubscribe(this.subscription); } }
- Q4. How would you implement a complex form with dynamic validation?
Ans: Dynamic form validation:import { LightningElement, track } from 'lwc'; export default class DynamicForm extends LightningElement { @track formData = { firstName: '', lastName: '', email: '', age: '', subscribe: false }; @track errors = {}; validationRules = { firstName: [ { required: true, message: 'First name is required' }, { pattern: /^[a-zA-Z]+$/, message: 'First name must contain only letters' } ], lastName: [ { required: true, message: 'Last name is required' } ], email: [ { required: true, message: 'Email is required' }, { pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'Invalid email format' } ], age: [ { required: true, message: 'Age is required' }, { min: 18, message: 'Must be at least 18 years old' }, { max: 120, message: 'Must be less than 120 years old' } ] }; handleInputChange(event) { const field = event.target.name; const value = event.target.value; this.formData[field] = value; // Validate field on change this.validateField(field, value); } validateField(field, value) { const rules = this.validationRules[field]; if (!rules) return; for (const rule of rules) { if (rule.required && !value) { this.errors[field] = rule.message; return; } if (rule.pattern && value && !rule.pattern.test(value)) { this.errors[field] = rule.message; return; } if (rule.min && value < rule.min) { this.errors[field] = rule.message; return; } if (rule.max && value > rule.max) { this.errors[field] = rule.message; return; } } // Clear error if validation passes delete this.errors[field]; } validateForm() { let isValid = true; // Validate all fields Object.keys(this.validationRules).forEach(field => { this.validateField(field, this.formData[field]); if (this.errors[field]) { isValid = false; } }); return isValid; } handleSubmit() { if (this.validateForm()) { // Submit form console.log('Form submitted:', this.formData); } else { console.log('Form has errors:', this.errors); } } }
- Q5. How would you implement dynamic component creation in LWC?
Ans: Dynamic component creation:import { LightningElement } from 'lwc'; import { createElement } from 'lwc'; // Import components dynamically import DynamicComponentA from 'c/dynamicComponentA'; import DynamicComponentB from 'c/dynamicComponentB'; export default class DynamicComponentCreator extends LightningElement { currentComponent = null; createComponent(componentType, props = {}) { let ComponentConstructor; switch (componentType) { case 'A': ComponentConstructor = DynamicComponentA; break; case 'B': ComponentConstructor = DynamicComponentB; break; default: throw new Error('Unknown component type'); } // Create component instance const element = createElement('c-dynamic-component', { is: ComponentConstructor }); // Set properties Object.assign(element, props); // Add event listeners element.addEventListener('customEvent', this.handleCustomEvent.bind(this)); return element; } handleCustomEvent(event) { console.log('Custom event received:', event.detail); } renderComponent(componentType, props = {}) { // Clear previous component if (this.currentComponent) { this.template.querySelector('.container').removeChild(this.currentComponent); } // Create new component this.currentComponent = this.createComponent(componentType, props); // Add to DOM this.template.querySelector('.container').appendChild(this.currentComponent); } renderComponentA() { this.renderComponent('A', { message: 'Hello from Component A' }); } renderComponentB() { this.renderComponent('B', { data: [1, 2, 3, 4, 5] }); } }
- Q6. How would you implement a search-as-you-type feature with debouncing?
Ans: Debounced search implementation:import { LightningElement, track } from 'lwc'; import searchRecords from '@salesforce/apex/RecordController.searchRecords'; export default class SearchAsYouType extends LightningElement { @track searchResults = []; @track isLoading = false; @track searchTerm = ''; searchTimeout; handleSearchChange(event) { const searchTerm = event.target.value; this.searchTerm = searchTerm; // Clear previous timeout if (this.searchTimeout) { clearTimeout(this.searchTimeout); } // Set new timeout this.searchTimeout = setTimeout(() => { this.performSearch(searchTerm); }, 300); // 300ms delay } async performSearch(searchTerm) { if (!searchTerm) { this.searchResults = []; return; } try { this.isLoading = true; this.searchResults = await searchRecords({ searchTerm }); } catch (error) { console.error('Search error:', error); this.searchResults = []; } finally { this.isLoading = false; } } // Cancel search if component is destroyed disconnectedCallback() { if (this.searchTimeout) { clearTimeout(this.searchTimeout); } } }
- Q7. How would you implement a drag-and-drop file uploader?
Ans: Drag-and-drop file uploader:import { LightningElement, track } from 'lwc'; import { ShowToastEvent } from 'lightning/platformShowToastEvent'; export default class DragDropUploader extends LightningElement { @track isDragOver = false; @track uploadedFiles = []; handleDragEnter(event) { event.preventDefault(); this.isDragOver = true; } handleDragOver(event) { event.preventDefault(); } handleDragLeave(event) { event.preventDefault(); this.isDragOver = false; } handleDrop(event) { event.preventDefault(); this.isDragOver = false; const files = event.dataTransfer.files; this.handleFiles(files); } handleFileInputChange(event) { const files = event.target.files; this.handleFiles(files); } handleFiles(files) { for (let i = 0; i < files.length; i++) { const file = files[i]; // Validate file type and size if (this.validateFile(file)) { this.uploadedFiles.push({ name: file.name, size: file.size, type: file.type, file: file }); } } } validateFile(file) { const maxFileSize = 5 * 1024 * 1024; // 5MB const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf']; if (file.size > maxFileSize) { this.dispatchEvent( new ShowToastEvent({ title: 'File too large', message: `${file.name} exceeds 5MB limit`, variant: 'error' }) ); return false; } if (!allowedTypes.includes(file.type)) { this.dispatchEvent( new ShowToastEvent({ title: 'Invalid file type', message: `${file.name} is not a supported file type`, variant: 'error' }) ); return false; } return true; } removeFile(event) { const index = event.target.dataset.index; this.uploadedFiles.splice(index, 1); } uploadFiles() { // Implement file upload logic this.uploadedFiles.forEach(fileObj => { this.uploadFile(fileObj.file); }); } async uploadFile(file) { // Implementation for uploading file to Salesforce // This would typically use lightning-file-upload or a custom Apex method } }
- Q8. How would you implement a responsive navigation menu?
Ans: Responsive navigation implementation:import { LightningElement, track } from 'lwc'; export default class ResponsiveNavigation extends LightningElement { @track isMenuOpen = false; @track isMobile = false; connectedCallback() { this.checkScreenSize(); window.addEventListener('resize', this.checkScreenSize.bind(this)); } checkScreenSize() { this.isMobile = window.innerWidth < 768; } toggleMenu() { this.isMenuOpen = !this.isMenuOpen; } closeMenu() { this.isMenuOpen = false; } handleNavigation(event) { const page = event.target.dataset.page; // Navigate to page this.navigateToPage(page); // Close menu on mobile if (this.isMobile) { this.closeMenu(); } } navigateToPage(page) { // Implementation for navigation console.log('Navigating to:', page); } get menuClass() { return `slds-nav-vertical ${this.isMenuOpen ? 'slds-is-expanded' : ''}`; } get menuButtonClass() { return `slds-button slds-button_icon slds-button_icon-container ${ this.isMenuOpen ? 'slds-is-selected' : '' }`; } disconnectedCallback() { window.removeEventListener('resize', this.checkScreenSize); } }
- Q9. How would you implement a complex charting solution in LWC?
Ans: Charting implementation with Chart.js:import { LightningElement, api, track } from 'lwc'; import { loadScript, loadStyle } from 'lightning/platformResourceLoader'; import chartjs from '@salesforce/resourceUrl/chartJs'; export default class ChartComponent extends LightningElement { @api chartType = 'bar'; @api chartData = []; @api chartLabels = []; @track isChartJsLoaded = false; chart; renderedCallback() { if (this.isChartJsLoaded) { this.renderChart(); } else { this.loadChartJs(); } } async loadChartJs() { try { await loadScript(this, chartjs + '/Chart.min.js'); await loadStyle(this, chartjs + '/Chart.min.css'); this.isChartJsLoaded = true; this.renderChart(); } catch (error) { console.error('Error loading Chart.js:', error); } } renderChart() { const canvas = this.template.querySelector('canvas'); if (!canvas) return; const ctx = canvas.getContext('2d'); // Destroy existing chart if it exists if (this.chart) { this.chart.destroy(); } // Create new chart this.chart = new Chart(ctx, { type: this.chartType, data: { labels: this.chartLabels, datasets: [{ label: 'Data', data: this.chartData, backgroundColor: 'rgba(54, 162, 235, 0.2)', borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true } } } }); } @api updateChartData(newData, newLabels) { this.chartData = newData; this.chartLabels = newLabels; if (this.chart) { this.chart.data.datasets[0].data = newData; this.chart.data.labels = newLabels; this.chart.update(); } } disconnectedCallback() { if (this.chart) { this.chart.destroy(); } } }
- Q10. How would you implement a multi-step form wizard?
Ans: Multi-step form wizard:import { LightningElement, track } from 'lwc'; export default class MultiStepForm extends LightningElement { @track currentStep = 1; @track formData = { step1: {}, step2: {}, step3: {} }; steps = [ { id: 1, name: 'Personal Info', completed: false }, { id: 2, name: 'Contact Info', completed: false }, { id: 3, name: 'Confirmation', completed: false } ]; get progress() { return (this.currentStep / this.steps.length) * 100; } get isLastStep() { return this.currentStep === this.steps.length; } get isFirstStep() { return this.currentStep === 1; } nextStep() { if (this.currentStep < this.steps.length) { this.markStepAsCompleted(this.currentStep); this.currentStep++; } } previousStep() { if (this.currentStep > 1) { this.currentStep--; } } markStepAsCompleted(stepId) { const step = this.steps.find(step => step.id === stepId); if (step) { step.completed = true; } } handleInputChange(event) { const field = event.target.name; const value = event.target.value; // Store data in current step this.formData[`step${this.currentStep}`][field] = value; } handleSubmit() { // Submit all form data console.log('Form submitted:', this.formData); // Reset form this.resetForm(); } resetForm() { this.currentStep = 1; this.formData = { step1: {}, step2: {}, step3: {} }; // Reset step completion status this.steps.forEach(step => { step.completed = false; }); } get step1Class() { return this.currentStep === 1 ? 'slds-show' : 'slds-hide'; } get step2Class() { return this.currentStep === 2 ? 'slds-show' : 'slds-hide'; } get step3Class() { return this.currentStep === 3 ? 'slds-show' : 'slds-hide'; } }