scenario-based React interview questions that assess your problem-solving skills and your ability to apply React concepts in real-world situations:
1. Scenario: Optimizing Performance in a Large Application
Question: You have a large React application with many components, and it is becoming slow due to unnecessary re-renders. How would you optimize the performance of this application?
Answer:
- Use React.memo for functional components and PureComponent for class components to prevent unnecessary re-renders.
- Implement shouldComponentUpdate lifecycle method for class components to control re-renders.
- Use useMemo and useCallback hooks to memoize values and functions that don’t need to be recalculated on each render.
- Implement code-splitting using React.lazy and Suspense to load only necessary components.
- Optimize images and assets by using lazy loading.
- Use React DevTools to identify performance bottlenecks.
2. Scenario: Managing API Calls in a Component
Question: You are building a React app that needs to fetch data from an API when the component mounts. How do you manage this API call and handle the loading, error, and success states?
Answer:
- Use the useEffect hook to make the API call when the component mounts.
- Maintain the API response in a state using useState.
- Handle loading and error states using additional state variables.
- Display a loading spinner while waiting for the data and show error messages if the API call fails.
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>Data: {JSON.stringify(data)}</div>;
3. Scenario: Handling Complex Forms with Validation
Question: You are tasked with building a form with complex validation, including checking for required fields, email format, and password strength. How would you implement this in React?
Answer:
- Use controlled components for form fields and manage the form state with useState.
- Implement custom validation functions for each field.
- Use libraries like Formik or React Hook Form to handle form state, validation, and submission.
- Display error messages next to the corresponding fields when validation fails.
- You can also use Yup for schema-based validation with Formik.
const [formData, setFormData] = useState({ email: '', password: '' });
const [errors, setErrors] = useState({ email: '', password: '' });
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
};
const validateForm = () => {
let errors = {};
if (!formData.email) errors.email = "Email is required";
if (!/\S+@\S+\.\S+/.test(formData.email)) errors.email = "Invalid email format";
if (formData.password.length < 8) errors.password = "Password must be at least 8 characters";
setErrors(errors);
return Object.keys(errors).length === 0;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validateForm()) {
console.log("Form submitted:", formData);
}
};
return (
<form onSubmit={handleSubmit}>
<input type="email" name="email" value={formData.email} onChange={handleChange} />
{errors.email && <div>{errors.email}</div>}
<input type="password" name="password" value={formData.password} onChange={handleChange} />
{errors.password && <div>{errors.password}</div>}
<button type="submit">Submit</button>
</form>
);
4. Scenario: Avoiding Prop Drilling
Question: You have a deeply nested component tree and you need to pass data from the top-most parent to a child component located at a deep level. How would you avoid “prop drilling”?
Answer:
- Use React Context to provide and consume data without passing props manually at every level.
- Define a context at the top level and wrap the components that need access to the data.
- Use the useContext hook in the child components to access the context values.
const UserContext = React.createContext();
const ParentComponent = () => {
const user = { name: "John Doe" };
return (
<UserContext.Provider value={user}>
<ChildComponent />
</UserContext.Provider>
);
};
const ChildComponent = () => {
const user = useContext(UserContext);
return <div>Hello, {user.name}</div>;
};
5. Scenario: Authentication and Protected Routes
Question: How would you implement a basic authentication system in a React app, ensuring that some routes are protected and only accessible by authenticated users?
Answer:
- Use React Context or a state management library like Redux to store authentication state (e.g., whether the user is logged in or not).
- Create a PrivateRoute component that checks the authentication state and either renders the protected component or redirects to the login page.
- Use React Router for navigation and routing.
const AuthContext = React.createContext();
const PrivateRoute = ({ children }) => {
const { isAuthenticated } = useContext(AuthContext);
return isAuthenticated ? children : <Redirect to="/login" />;
};
const App = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return (
<AuthContext.Provider value={{ isAuthenticated, setIsAuthenticated }}>
<BrowserRouter>
<Route path="/login" component={Login} />
<PrivateRoute path="/dashboard">
<Dashboard />
</PrivateRoute>
</BrowserRouter>
</AuthContext.Provider>
);
};
6. Scenario: Lazy Loading Components
Question: You have a React app that contains several large components. How would you implement lazy loading to improve performance?
Answer:
- Use React.lazy and Suspense to lazy-load components only when they are needed.
- Wrap the lazy-loaded component in a Suspense component to display a fallback UI (like a spinner) while the component is being loaded.
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
const App = () => (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
7. Scenario: Error Handling in React
Question: You need to catch errors in your React components and display a fallback UI without crashing the entire application. How would you implement this?
Answer:
- Use Error Boundaries to catch JavaScript errors in their child component tree, log those errors, and display a fallback UI.
- Error boundaries are implemented by creating a class component that defines
componentDidCatchlifecycle method.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
console.log(error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong!</h1>;
}
return this.props.children;
}
}
const App = () => (
<ErrorBoundary>
<SomeComponent />
</ErrorBoundary>
);
8. Scenario: Conditional Rendering
Question:
You have a component that displays a user’s profile. If the user is logged in, it should show their name and a logout button. If the user is not logged in, it should show a login button. How would you implement this in React?
Answer:
function UserProfile({ isLoggedIn, userName, onLogin, onLogout }) {
return (
<div>
{isLoggedIn ? (
<div>
<p>Welcome, {userName}!</p>
<button onClick={onLogout}>Logout</button>
</div>
) : (
<button onClick={onLogin}>Login</button>
)}
</div>
);
}
Explanation:
- Use a ternary operator to conditionally render content based on the
isLoggedInprop. - If
isLoggedInistrue, display the user’s name and a logout button. - If
isLoggedInisfalse, display a login button.
9. Scenario: Lifting State Up
Question:
You have two sibling components: InputComponent and DisplayComponent. The InputComponent should take user input, and the DisplayComponent should display it. How would you implement this?
Answer:
import React, { useState } from 'react';
function ParentComponent() {
const [inputValue, setInputValue] = useState('');
const handleInputChange = (e) => {
setInputValue(e.target.value);
};
return (
<div>
<InputComponent value={inputValue} onChange={handleInputChange} />
<DisplayComponent value={inputValue} />
</div>
);
}
function InputComponent({ value, onChange }) {
return (
<input type="text" value={value} onChange={onChange} />
);
}
function DisplayComponent({ value }) {
return <p>You typed: {value}</p>;
}
Explanation:
- Lift the state up to the parent component (
ParentComponent). - Pass the state and handler function as props to
InputComponent. - Pass the state as a prop to
DisplayComponent.
10. Scenario: React Context API
Question:
You need to share a theme (light/dark) across multiple components without prop drilling. How would you implement this using the React Context API?
Answer:
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button onClick={toggleTheme} style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
Toggle Theme
</button>
);
}
function App() {
return (
<ThemeProvider>
<ThemedButton />
</ThemeProvider>
);
}
Explanation:
- Create a
ThemeContextusingcreateContext. - Use a
ThemeProviderto wrap the components that need access to the theme. - Use the
useContexthook to consume the context in child components.
11. Scenario: React Router
Question:
You need to create a simple app with two pages: a Home page and an About page. How would you implement this using React Router?
Answer:
import React from 'react';
import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom';
function Home() {
return <h1>Home Page</h1>;
}
function About() {
return <h1>About Page</h1>;
}
function App() {
return (
<Router>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
}
Explanation:
- Use
react-router-domto set up routing. - Define routes using the
RoutesandRoutecomponents. - Use the
Linkcomponent for navigation.
These scenarios cover a range of real-world challenges you may face while working with React.