REACT Scenario-Based Questions

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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>;
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>;
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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
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> );
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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>;
};
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>; };
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.

 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
};
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> ); };
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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
const App = () => (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
const HeavyComponent = React.lazy(() => import('./HeavyComponent')); const App = () => ( <Suspense fallback={<div>Loading...</div>}> <HeavyComponent /> </Suspense> );
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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
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> );
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
}
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> ); }
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 is true, display the user’s name and a logout button.
  • If isLoggedIn is false, 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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>;
}
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>; }
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
}
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> ); }
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 using createContext.
  • 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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
}
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> ); }
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 and Route components.
  • Use the Link component for navigation.

 

These scenarios cover a range of real-world challenges you may face while working with React.