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
componentDidCatch
lifecycle 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
isLoggedIn
prop. - If
isLoggedIn
istrue
, display the user’s name and a logout button. - If
isLoggedIn
isfalse
, 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
ThemeContext
usingcreateContext
. - Use a
ThemeProvider
to wrap the components that need access to the theme. - Use the
useContext
hook 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-dom
to set up routing. - Define routes using the
Routes
andRoute
components. - Use the
Link
component for navigation.
These scenarios cover a range of real-world challenges you may face while working with React.