Top 20 React JS Interview Questions

Master these questions with in-depth explanations and code examples to ace your next React interview!

Start Your Journey

1. What is React?

React is an open-source JavaScript library developed by Facebook for building user interfaces, particularly for single-page applications (SPAs). It allows developers to create reusable UI components and efficiently update the DOM using a Virtual DOM. Unlike frameworks like Angular, React focuses solely on the view layer, giving developers flexibility to integrate it with other libraries or tools.

React's key features include its declarative nature (you describe what the UI should look like for a given state), component-based architecture, and unidirectional data flow. It’s widely used because of its performance and scalability.

// Basic React component
import React from 'react';
import ReactDOM from 'react-dom';

function App() {
  return (
    <div>
      <h1>Hello, React!</h1>
      <p>This is a simple React component.</p>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

In this example, the `App` component is rendered into a DOM element with the ID `root`. React handles the rendering and updates efficiently using the Virtual DOM.

2. What are components in React?

Components are the building blocks of a React application. They encapsulate UI logic and can be either functional (using hooks) or class-based (using lifecycle methods). Components promote reusability, modularity, and maintainability.

Functional components are simpler and more modern, while class components were traditionally used for state management and lifecycle methods before hooks were introduced in React 16.8.

// Functional Component
const Welcome = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};

// Class Component
class WelcomeClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = { greeting: 'Hello' };
  }
  render() {
    return <h1>{this.state.greeting}, {this.props.name}!</h1>;
  }
}

// Usage
function App() {
  return (
    <>
      <Welcome name="Alice" />
      <WelcomeClass name="Bob" />
    </>
  );
}

Here, `Welcome` is a functional component that accepts a `name` prop, while `WelcomeClass` is a class component with internal state. Both achieve similar results but differ in syntax and capabilities.

3. What is JSX?

JSX (JavaScript XML) is a syntax extension that allows you to write HTML-like code within JavaScript. It’s not HTML but gets transpiled into `React.createElement` calls by tools like Babel. JSX makes React code more readable and intuitive.

JSX supports embedding JavaScript expressions using curly braces `` and can include attributes, conditionals, and loops.

// JSX Example
const name = "React";
const element = (
  <div className="container">
    <h1>Hello, {name}!</h1>
    {true ? <p>JSX is awesome!</p> : <p>Not rendered</p>}
  </div>
);

// Transpiled to:
const element = React.createElement(
  "div",
  { className: "container" },
  React.createElement("h1", null, "Hello, ", name, "!"),
  true ? React.createElement("p", null, "JSX is awesome!") : React.createElement("p", null, "Not rendered")
);

This shows how JSX simplifies UI declaration. The transpiled version demonstrates what happens under the hood, but developers rarely write it manually.

4. What is the Virtual DOM?

The Virtual DOM is an in-memory representation of the real DOM. React uses it to optimize updates by minimizing direct manipulations of the actual DOM, which is slow. When state or props change, React creates a new Virtual DOM, compares it with the previous one (diffing), and updates only the changed parts.

This process, called reconciliation, enhances performance, especially in dynamic applications with frequent updates.

import React, { useState } from 'react';
      
      function Counter() {
        const [count, setCount] = useState(0);
      
        return (
          <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>Click me</button>
          </div>
        );
      }
      
      // React updates only the <p> content when count changes
      

In this example, clicking the button updates the count state. React re-renders the Virtual DOM, detects the change in the <p> element, and updates only that part in the real DOM.

5. What are props in React?

Props are inputs to React components, allowing data to flow from parent to child. They are immutable within the receiving component, ensuring a unidirectional data flow. Props can be anything: strings, numbers, objects, functions, or even JSX.

They enable component reusability by making them configurable from the outside.

// Child component
const Greeting = ({ name, greetFn }) => (
  <button onClick={() => greetFn(name)}>Hello, {name}!</button>
);

// Parent component
function App() {
  const sayHello = (name) => alert(`Hi, ${name}!`);
  return (
    <div>
      <Greeting name="Alice" greetFn={sayHello} />
      <Greeting name="Bob" greetFn={sayHello} />
    </div>
  );
}

Here, `Greeting` receives `name` and `greetFn` as props. The parent `App` passes different names and a shared function, demonstrating how props customize behavior.

6. What is state in React?

State is a mutable object that holds data a component needs to render dynamically. When state changes, React re-renders the component to reflect the new state. In functional components, state is managed with the `useState` hook; in class components, it’s managed via `this.state`.

State is private to the component and can only be updated using the provided setter function (e.g., `setState` or the updater from `useState`).

import React, { useState } from 'react';

function Toggle() {
  const [isOn, setIsOn] = useState(false);

  return (
    <div>
      <p>Status: {isOn ? 'On' : 'Off'}</p>
      <button onClick={() => setIsOn(!isOn)}>
        Toggle
      </button>
    </div>
  );
}

// Class version
class ToggleClass extends React.Component {
  state = { isOn: false };
  toggle = () => this.setState({ isOn: !this.state.isOn });
  render() {
    return (
      <div>
        <p>Status: {this.state.isOn ? 'On' : 'Off'}</p>
        <button onClick={this.toggle}>Toggle</button>
      </div>
    );
  }
}

Both examples show a toggle switch. The functional version uses `useState`, while the class version uses `setState`. State changes trigger re-renders automatically.

7. What are hooks in React?

Hooks are special functions introduced in React 16.8 that let you use state and lifecycle features in functional components. They eliminate the need for class components in many cases, making code cleaner and more reusable.

Common hooks include `useState` (for state), `useEffect` (for side effects), and `useContext` (for context). Custom hooks can also be created for shared logic.

import React, { useState, useEffect } from 'react';

// Custom hook
function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  return width;
}

function App() {
  const width = useWindowWidth();
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Window width: {width}px</p>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

This example uses `useState` for a counter, `useEffect` in a custom `useWindowWidth` hook to track window size, and demonstrates how hooks simplify stateful logic in functional components.

8. What is the useEffect hook?

The `useEffect` hook manages side effects in functional components, such as data fetching, subscriptions, or DOM manipulations. It runs after every render by default but can be controlled with a dependency array.

It replaces lifecycle methods like `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount` in class components. The cleanup function (returned by `useEffect`) prevents memory leaks.

import React, { useState, useEffect } from 'react';

function DataFetcher({ url }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (error) {
        console.error('Fetch error:', error);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, [url]); // Runs when url changes

  return (
    <div>
      {loading ? <p>Loading...</p> : <pre>{JSON.stringify(data, null, 2)}</pre>}
    </div>
  );
}

function App() {
  return <DataFetcher url="https://api.example.com/data" />;
}

Here, `useEffect` fetches data when the `url` prop changes. The dependency array `[url]` ensures it doesn’t run unnecessarily, and the async operation is handled cleanly.

9. What is the difference between useState and useReducer?

`useState` is a simple hook for managing independent state variables, while `useReducer` is suited for complex state logic involving multiple values or interdependent updates. `useReducer` uses a reducer function (inspired by Redux) to handle state transitions.

Use `useState` for basic state (e.g., a single counter). Use `useReducer` when state updates depend on previous state or involve multiple actions (e.g., a form with many fields).

import React, { useState, useReducer } from 'react';

// useState example
function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Add</button>
    </div>
  );
}

// useReducer example
const initialState = { count: 0, step: 1 };
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + state.step };
    case 'setStep':
      return { ...state, step: action.step };
    default:
      return state;
  }
}

function AdvancedCounter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      <p>Count: {state.count}</p>
      <p>Step: {state.step}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Add</button>
      <input
        type="number"
        value={state.step}
        onChange={(e) => dispatch({ type: 'setStep', step: Number(e.target.value) })}
      />
    </div>
  );
}

`useState` is straightforward for a single `count`. `useReducer` manages `count` and `step` together, allowing more complex interactions like adjusting the increment step dynamically.

10. What is React Router?

React Router is a library for handling client-side routing in React applications. It enables navigation between views (components) without full page reloads, mimicking the behavior of a multi-page app in an SPA.

It provides components like `BrowserRouter`, `Route`, `Link`, and `Switch` (or `Routes` in v6) to define routes and navigate programmatically or via links.

import { BrowserRouter, Route, Routes, Link } from 'react-router-dom';

function Home() {
  return <h1>Home Page</h1>;
}

function About() {
  return <h1>About Page</h1>;
}

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link> | <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

This example sets up a simple app with two routes. Clicking the `Link` components updates the URL and renders the corresponding component without refreshing the page.

11. What is the purpose of keys in React?

Keys are unique identifiers assigned to elements in a list to help React efficiently update the DOM. They ensure React can track which items have changed, been added, or removed during re-renders, avoiding unnecessary DOM operations.

Without keys, React might re-render the entire list or misalign elements, leading to performance issues or bugs. Use stable IDs (e.g., from data) rather than array indices when possible.

import React, { useState } from 'react';
      
      function TodoList() {
        const [todos, setTodos] = useState([
          { id: 1, text: 'Learn React' },
          { id: 2, text: 'Build a project' },
        ]);
      
        const addTodo = () => {
          setTodos([...todos, { id: Date.now(), text: 'New Todo' }]);
        };
      
        return (
          <div>
            <button onClick={addTodo}>Add Todo</button>
            <ul>
              {todos.map(todo => (
                <li key={todo.id}>{todo.text}</li>
              ))}
            </ul>
          </div>
        );
      }

Here, key={todo.id} ensures React tracks each <li> by its unique id. If a todo is added, only the new item is appended, not the entire list re-rendered.

12. What is a controlled component?

A controlled component is a form element whose value is controlled by React state. The component’s value is synced with state, and updates occur via event handlers, ensuring a single source of truth.

This approach is predictable and allows validation or formatting before updating the UI. It’s commonly used for inputs, selects, and textareas.

import React, { useState } from 'react';

function Form() {
  const [name, setName] = useState('');
  const [submitted, setSubmitted] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    setSubmitted(name);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value.toUpperCase())}
        />
      </label>
      <button type="submit">Submit</button>
      <p>Submitted: {submitted}</p>
    </form>
  );
}

The input’s `value` is tied to `name` state, and `onChange` updates it (converting to uppercase as an example). On submit, the state value is displayed, showing full control by React.

13. What is an uncontrolled component?

An uncontrolled component manages its own state internally, typically via the DOM, and React accesses its value using refs. This is less common but useful when integrating with non-React libraries or for simpler forms.

Unlike controlled components, the state isn’t mirrored in React; you retrieve values when needed (e.g., on submit).

import React, { useRef } from 'react';

function UncontrolledForm() {
  const inputRef = useRef(null);
  const [submitted, setSubmitted] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    setSubmitted(inputRef.current.value);
    inputRef.current.value = ''; // Reset manually
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" ref={inputRef} defaultValue="Initial" />
      </label>
      <button type="submit">Submit</button>
      <p>Submitted: {submitted}</p>
    </form>
  );
}

Here, `inputRef` accesses the input’s DOM node. The value isn’t tracked in state during typing; it’s grabbed on submit. `defaultValue` sets an initial value.

14. What is the Context API?

The Context API provides a way to share data (state) across the component tree without passing props manually at every level (prop drilling). It’s useful for global data like themes, user info, or settings.

It consists of a `Provider` (to supply the value) and a `Consumer` (or `useContext` hook) to access it. Context should be used sparingly to avoid overcomplicating the app.

import React, { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return <Button />;
}

function Button() {
  const { theme, setTheme } = useContext(ThemeContext);
  return (
    <button
      style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}
      onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
    >
      Toggle Theme: {theme}
    </button>
  );
}

The `ThemeContext` provides `theme` and `setTheme` to all descendants. The `Button` component consumes it with `useContext` to toggle between light and dark themes.

15. What is Redux?

Redux is a predictable state management library for JavaScript apps, often paired with React. It centralizes state in a single store, making it easier to track changes and debug. It follows three principles: single source of truth, state is read-only, and changes are made with pure functions (reducers).

Redux is overkill for small apps but shines in large-scale applications with complex state interactions.

import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';

// Reducer
const counterReducer = (state = { count: 0 }, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

// Store
const store = createStore(counterReducer);

// React component
function Counter() {
  const count = useSelector(state => state.count);
  const dispatch = useDispatch();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Add</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Subtract</button>
    </div>
  );
}

function App() {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
}

The `counterReducer` manages the state. `useSelector` accesses the `count`, and `useDispatch` sends actions to update it. The `Provider` makes the store available to all components.

16. What are React Fragments?

React Fragments let you group multiple elements without adding extra DOM nodes (like a <div>). This keeps the DOM cleaner and avoids unnecessary wrappers that might affect styling or semantics.

You can use the shorthand <>...</> or the explicit <React.Fragment> syntax, the latter supporting keys for lists.

import React from 'react';
      
      function List() {
        const items = ['Apple', 'Banana', 'Orange'];
      
        return (
          <>
            <h1>Fruits</h1>
            <ul>
              {items.map((item, index) => (
                <React.Fragment key={index}>
                  <li>{item}</li>
                  <p>Item #{index + 1}</p>
                </React.Fragment>
              ))}
            </ul>
          </>
        );
      }

The shorthand <>...</> wraps the <h1> and <ul>, while <React.Fragment> with a key groups each <li> and <p> in the list, avoiding an extra parent <div>.

17. What is the useMemo hook?

The `useMemo` hook memoizes expensive computations, preventing them from re-running on every render unless their dependencies change. It’s a performance optimization tool, not a guarantee of execution.

Use it for calculations that are costly (e.g., filtering large arrays) and depend on specific values.

import React, { useState, useMemo } from 'react';

function ExpensiveComponent() {
  const [count, setCount] = useState(0);
  const [other, setOther] = useState(0);

  const expensiveCalculation = (num) => {
    console.log('Calculating...');
    return Array(num).fill().reduce((acc) => acc + Math.random(), 0);
  };

  const memoizedValue = useMemo(() => expensiveCalculation(count), [count]);

  return (
    <div>
      <p>Memoized Value: {memoizedValue}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <button onClick={() => setOther(other + 1)}>Increment Other: {other}</button>
    </div>
  );
}

`expensiveCalculation` only runs when `count` changes, not `other`. Without `useMemo`, it would run on every render, slowing the app unnecessarily.

18. What is the useCallback hook?

The `useCallback` hook memoizes callback functions, ensuring they don’t change unless their dependencies do. This prevents unnecessary re-renders in child components that rely on the callback via props.

It’s often used with `React.memo` to optimize performance in components with event handlers.

import React, { useState, useCallback } from 'react';

const Child = React.memo(({ onClick }) => {
  console.log('Child rendered');
  return <button onClick={onClick}>Click me</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  const [other, setOther] = useState(0);

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]); // Only recreates if count changes

  return (
    <div>
      <p>Count: {count}</p>
      <Child onClick={handleClick} />
      <button onClick={() => setOther(other + 1)}>Other: {other}</button>
    </div>
  );
}

`handleClick` is memoized with `useCallback`. The `Child` (wrapped in `React.memo`) only re-renders if `onClick` changes, not when `other` updates, improving performance.

19. What are React Portals?

React Portals allow rendering children into a DOM node outside the parent component’s hierarchy, such as the <body> for modals or tooltips. This avoids issues with CSS (e.g., overflow: hidden) or DOM stacking contexts.

Portals still participate in React’s event bubbling and context, despite being outside the parent DOM tree.

import React, { useState } from 'react';
      import { createPortal } from 'react-dom';
      
      function Modal({ children }) {
        return createPortal(
          <div style={{ position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', background: '#fff', padding: '20px' }}>
            {children}
          </div>,
          document.getElementById('modal-root') // Assumes <div id="modal-root"></div> in HTML
        );
      }
      
      function App() {
        const [showModal, setShowModal] = useState(false);
      
        return (
          <div>
            <button onClick={() => setShowModal(true)}>Show Modal</button>
            {showModal && (
              <Modal>
                <h2>Modal Title</h2>
                <button onClick={() => setShowModal(false)}>Close</button>
              </Modal>
            )}
          </div>
        );
      }

The <Modal> component renders into #modal-root outside the <App> DOM tree, ensuring it’s positioned correctly (e.g., over other content) without parent constraints.

20. What is lazy loading in React?

Lazy loading defers loading of components until they’re needed, reducing initial bundle size and improving load time. React provides `React.lazy` and `Suspense` for this purpose, commonly used for code splitting routes or heavy components.

It’s especially useful in large applications to optimize performance by loading only what the user currently needs.

import React, { lazy, Suspense } from 'react';
import { BrowserRouter, Route, Routes, Link } from 'react-router-dom';

// Lazy-loaded component
const HeavyComponent = lazy(() => import('./HeavyComponent')); // Assume this is a separate file

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link> | <Link to="/heavy">Heavy</Link>
      </nav>
      <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/" element={<h1>Home</h1>} />
          <Route path="/heavy" element={<HeavyComponent />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

// HeavyComponent.js
export default function HeavyComponent() {
  return <h1>This is a heavy component!</h1>;
}

`HeavyComponent` is loaded only when the `/heavy` route is accessed. `Suspense` shows a fallback UI during loading, enhancing user experience while splitting the bundle.