Frontend Interview Questions:


Identify issues in the following code
Question1 :
The Code:
async function fetchData(url: string): Promise<any> {
const response = await fetch(url);
const { data } = await response.json();
return { data };
}
const result = fetchData('https://api.example.com/data');
console.log(result.data);
Issues Identified:
Missing error handling - Network requests can fail, but there's no try-catch block
Logging undefined data - The result is a Promise, not the actual data(await is missing)
Improved Version:
async function fetchData(url: string): Promise<any> {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const { data } = await response.json();
return { data };
} catch (error) {
console.error('Failed to fetch data:', error);
throw error; // Re-throw to allow caller to handle
}
}
// Proper usage
const result = await fetchData('https://api.example.com/data'); // AWAIT MISSING
console.log(result.data);
Question 2:
The Code:
import { useEffect, useState } from 'react';
function DataList() {
const [data, setData] = useState([]);
useEffect(() => {
fetch('/api/data')
.then(response => response.json())
.then(data => setData(data));
}, [data]);
return (
<ul>
{data.map((item, index) => (
<li key={index}>{item.name}</li>
))}
</ul>
);
}
Critical Issues:
Infinite re-render loop - Including
data
in the dependency array causes the effect to run every time data changesPoor key prop - Using array index as key can cause rendering issues
Fixed Version:
import { useEffect, useState } from 'react';
function DataList() {
const [data, setData] = useState([]);
useEffect(() => {
fetch('/api/data')
.then(response => response.json())
.then(data => setData(data));
}, []);
return (
<ul>
{data.map((item, index) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Question 3:
The Code:
let data = [];
function fetchData() {
for (let i = 0; i < 1000000; i++) {
data.push({ id: i, value: `Item ${i}` });
}
}
function processData() {
data.forEach(item => {
console.log(item.value);
});
}
fetchData();
processData();
Major Issues:
Memory leak - Global array keeps growing indefinitely
Blocking main thread - Synchronous processing of 1M items
Inefficient console.log - Logging massive amounts of data
Optimized Solutions:
Approach 1: Chunked Processing
function fetchData() {
// Return data instead of storing globally
const data = [];
for (let i = 0; i < 1000000; i++) {
data.push({ id: i, value: `Item ${i}` });
}
return data;
}
function processDataInChunks(data, chunkSize = 1000) {
return new Promise((resolve) => {
let index = 0;
function processChunk() {
const chunk = data.slice(index, index + chunkSize);
chunk.forEach(item => {
// Process item (avoid console.log for performance)
// Do actual work here
});
index += chunkSize;
if (index < data.length) {
// Use requestIdleCallback for better performance
requestIdleCallback(processChunk);
} else {
resolve();
}
}
processChunk();
});
}
// Usage
const data = fetchData();
processDataInChunks(data).then(() => {
console.log('Processing complete');
// Clear data when done
data.length = 0;
});
Question 4:
The Code:
import React, { useState } from 'react';
const ComplexComponent = () => {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const handleClick = () => {
setCount(count + 1);
};
const handleChange = (e) => {
setText(e.target.value);
};
return (
<div>
<button onClick={handleClick}>Increment</button>
<input type="text" value={text} onChange={handleChange} />
<ExpensiveComponent count={count} />
</div>
);
};
const ExpensiveComponent = ({ count }) => {
console.log('Expensive component rendered');
return <div>Count: {count}</div>;
};
Issues:
Unnecessary re-renders -
ExpensiveComponent
re-renders whentext
orcount
changesFunction recreation - Event handlers are recreated on every render
Optimized Version:
import React, { useState, useCallback, memo } from 'react';
const ComplexComponent = () => {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
const handleChange = useCallback((e) => {
setText(e.target.value);
}, []);
return (
<div>
<button onClick={handleClick}>Increment</button>
<input type="text" value={text} onChange={handleChange} />
<ExpensiveComponent count={count} />
</div>
);
};
// Memoize the expensive component
const ExpensiveComponent = memo(({ count }) => {
console.log('Expensive component rendered');
return <div>Count: {count}</div>;
});
Final Thoughts
These interview questions weren't just about finding bugs—they were designed to test understanding of fundamental performance concepts that directly impact user experience. The key is not just knowing these patterns, but understanding why they cause issues and when to apply specific optimizations.
Remember: premature optimization is the root of all evil, but understanding these patterns helps you write performant code from the start and avoid common pitfalls that can seriously impact your application's performance.
What performance bottlenecks have you encountered in your frontend work? Share your experiences in the comments below!
If you found this helpful, follow me for more frontend development insights and interview preparation tips.
Subscribe to my newsletter
Read articles from kiran ashok directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
