DEV Community

Cover image for Most Commonly Asked React Interview Questions 2024 - Part 2
Sayuj Sehgal
Sayuj Sehgal

Posted on

Most Commonly Asked React Interview Questions 2024 - Part 2

If you like this blog, you can visit my personal blog sehgaltech for more content.

1. Explain the lifecycle methods in React?

Lifecycle methods in React are special methods that automatically get called as part of the lifecycle of a component. They allow you to control what happens when a component mounts, updates, or unmounts. These methods are only available in class components. However, with the introduction of Hooks in React 16.8, you can mimic their behavior in functional components.

Here are the most commonly used lifecycle methods:

  1. constructor(props): This is the method where you initialize state and bind methods. It's called before the component is mounted.

  2. componentDidMount(): This method is called once the component has been mounted to the DOM. It's a good place to make network requests or set up subscriptions.

  3. shouldComponentUpdate(nextProps, nextState): This method is called before a re-render and can be used to optimize performance by preventing unnecessary re-renders. It should return a boolean value.

  4. render(): This is the only required method in a class component. It should return JSX.

  5. componentDidUpdate(prevProps, prevState): This method is called after the component updates. It's a good place to perform operations that need the updated state or props.

  6. componentWillUnmount(): This method is called before the component is unmounted and destroyed. It's a good place to clean up any ongoing processes like network requests or timers.

Here's an example of a class component with lifecycle methods:


import React from 'react';

class MyClassComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  componentDidMount() {
    console.log('Component mounted');
  }

  shouldComponentUpdate(nextProps, nextState) {
    // Only update if the count has changed
    return nextState.count !== this.state.count;
  }

  componentDidUpdate(prevProps, prevState) {
    console.log('Component updated');
  }

  componentWillUnmount() {
    console.log('Component will unmount');
  }

  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

export default MyClassComponent;

Enter fullscreen mode Exit fullscreen mode

In functional components, you can use the useEffect Hook to mimic the behavior of componentDidMount, componentDidUpdate, and componentWillUnmount. Here's an example:


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

function MyFunctionalComponent(props) {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('Component mounted/updated');

    return () => {
      console.log('Component will unmount');
    };
  }, [count]); // Only re-run the effect if count changes

  return <h1>Hello, {props.name}</h1>;
}

export default MyFunctionalComponent;

Enter fullscreen mode Exit fullscreen mode

2. How do you handle state in React? What is the difference between state and props?

In React, state is a built-in object that contains data that may change over the lifetime of the component. State is managed within the component (local state), and when it changes, the component re-renders.

In class components, you initialize state in the constructor and update it using the setState method. Here's an example:


class MyClassComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  incrementCount = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={this.incrementCount}>
          Click me
        </button>
      </div>
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

In functional components, you can use the useState Hook to add state. useState returns a pair: the current state value and a function that lets you update it. Here's an example:


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>
  );
}

Enter fullscreen mode Exit fullscreen mode

Props (short for properties) are read-only and allow you to pass data from a parent component down to a child component. They are similar to function parameters. Here's an example:


function ChildComponent(props) {
  return <h1>Hello, {props.name}</h1>;
}

function ParentComponent() {
  return <ChildComponent name="John" />;
}

Enter fullscreen mode Exit fullscreen mode

The main differences between state and props are:

  • State is mutable and managed within the component, while props are read-only and passed from a parent component.
  • State changes can cause re-renders, while changes in props cause a re-render of the component and all its child components.
  • State is used when a component has data that changes over time, while props allow components to use and share data with other components.

3. What is Redux? How does it work with React?

Redux is a predictable state container for JavaScript applications. It's commonly used with React, but it can be used with any other JavaScript framework or library. It is used for managing the state of your application.

Redux maintains the state of an entire application in a single immutable state tree (object), which can't be changed directly. When something changes, a new object is created (using actions and reducers).

Here's a brief overview of how Redux works:

  1. Actions: Actions are plain JavaScript objects that have a 'type' field. They are the only way you can send data from your application to your Redux store. The 'type' field should be a string that gives this action a name, like 'USER_CLICKED_SUBMIT'. Actions usually have two properties: 'type' and 'payload'. The 'payload' is the data that you want to store in the state.

  2. Reducers: Reducers are functions that take the current state of the application and an action, then return a new state. They are pure functions, meaning they don't modify the input, don't produce side effects, and given the same input, will always produce the same output.

  3. Store: The Store is the object that brings Actions and Reducers together. The store has several responsibilities: holds application state, allows access to state via getState(), allows state to be updated via dispatch(action), registers listeners via subscribe(listener), and handles unregistering of listeners via the function returned by subscribe(listener).

In a React-Redux app, you'll also deal with the following:

  1. Provider: The <Provider /> makes the Redux store available to any nested components that have been wrapped in the connect() function.

  2. Connect: The connect() function connects a React component to the Redux store. It provides its connected component with the pieces of the data it needs from the store, and the functions it can use to dispatch actions to the store.

Redux is not always necessary and can add complexity to your application. It's best used in larger applications where multiple components need access to the same slices of state, or in situations where state needs to be managed in a more predictable way.

4. How do you handle forms in React?

In React, you can handle forms by creating controlled components. A controlled component is a component where React is in control of the form data. In a controlled component, the form data is handled by the state within the component.

Here's a simple example of a controlled component:


import React from 'react';

class MyForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: '' };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({ value: event.target.value });
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

export default MyForm;

Enter fullscreen mode Exit fullscreen mode

In this example, the form data is stored in the component's state and updated in real-time as the user types into the form field. The handleChange method is called every time the input field changes, updating the state with the new value. The handleSubmit method is called when the form is submitted.

For functional components, you can use the useState hook to manage form state:


import React, { useState } from 'react';

function MyForm() {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  }

  const handleSubmit = (event) => {
    alert('A name was submitted: ' + value);
    event.preventDefault();
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" value={value} onChange={handleChange} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
}

export default MyForm;

Enter fullscreen mode Exit fullscreen mode

In this functional component, the useState hook is used to manage the form state. The handleChange and handleSubmit functions work the same way as in the class component example.

5. What are higher-order components? Can you give an example?

Higher-order components (HOCs) in React are a pattern derived from React's compositional nature. HOCs are a function that takes a component and returns a new component with additional props or behaviors. They are a way to reuse component logic, and can be thought of as parameterized container components.

Here's a simple example of a higher-order component:


// A higher-order component that injects props into a component
function withExtraProps(WrappedComponent, extraProps) {
  return function(props) {
    return <WrappedComponent {...props} {...extraProps} />;
  };
}

// Usage
function MyComponent(props) {
  return <div>{props.message}</div>;
}

const MyComponentWithExtraProps = withExtraProps(MyComponent, { message: 'Hello, world!' });

// Now <MyComponentWithExtraProps /> will render 'Hello, world!'

Enter fullscreen mode Exit fullscreen mode

In this example, withExtraProps is a higher-order component. It's a function that takes a component (WrappedComponent) and some extraProps, and returns a new component that renders the WrappedComponent with its original props and the extraProps merged together.

This is a very simple example. In practice, HOCs can be used for a variety of tasks, such as connecting a component to a Redux store, adding lifecycle methods to a functional component, handling context, etc.

6. How do you handle routing in React?

In React, routing is handled by a library called React Router. React Router is a collection of navigational components that compose declaratively with your application. It keeps your UI in sync with the URL.

Here's a basic example of how to use React Router:

First, you need to install it using npm or yarn:


npm install react-router-dom

Enter fullscreen mode Exit fullscreen mode

or


yarn add react-router-dom

Enter fullscreen mode Exit fullscreen mode

Then, you can use it in your app:


import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import HomePage from './HomePage';
import AboutPage from './AboutPage';
import NotFoundPage from './NotFoundPage';

function App() {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={HomePage} />
        <Route path="/about" component={AboutPage} />
        <Route component={NotFoundPage} />
      </Switch>
    </Router>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

In this example, BrowserRouter is a component that uses the HTML5 history API to keep your UI in sync with the URL. Route is a component that renders some UI when its path matches the current URL. Switch is used to render only the first Route or Redirect that matches the location.

When the URL is "/", HomePage component will be rendered. When the URL is "/about", AboutPage component will be rendered. If the URL does not match any of the paths defined in the Route components, the NotFoundPage component will be rendered.

7. How do you optimize performance in a React application?

Optimizing performance in a React application can be achieved through several strategies:

  1. Use the Production Build: React includes a development build with helpful warnings. However, it is slower and larger than the production build. Always use the production build for the best performance in a live environment.

  2. Virtualize Long Lists: If you are rendering long lists of data, the browser can slow down significantly. Libraries like react-window or react-virtualized can help by only rendering items that are currently visible.

  3. Avoid Inline Function Definition in Render: Each time a component re-renders, a new function instance is created when you define a function inline in render. This can cause child components to re-render unnecessarily. Instead, define functions in the component class or use useCallback hook for functional components.

  4. Use shouldComponentUpdate/PureComponent/Memo: These features allow you to prevent unnecessary re-renders when the props or state haven't changed. shouldComponentUpdate is a lifecycle method in class components, PureComponent is a class component that implements shouldComponentUpdate with a shallow prop and state comparison, and React.memo is a higher order component that does the same thing for functional components.

  5. Lazy Loading: With React.lazy and Suspense, you can easily code-split and lazy-load different parts of your application to reduce the initial load time.

  6. Optimize Images: Use optimized, compressed images and consider using a CDN for serving images.

  7. Use Keys in Lists: When rendering lists, always use keys. Keys help React identify which items have changed, are added, or are removed and help in efficient re-renders.

  8. Debounce or Throttle High-Frequency Functions: Functions like event handlers that get triggered often can be debounced or throttled to prevent excessive function calls.

  9. Use the Profiler in React DevTools: This tool can help you identify performance bottlenecks in your application.

  10. Immutable Data Structures: Using libraries like Immutable.js can help optimize performance by reducing the complexity of tracking changes and determining when re-renders are necessary.

Remember, premature optimization can lead to unnecessary complexity. Always measure and understand where the real performance bottlenecks are before optimizing.

8. What is context in React? How do you use it?

Context in React provides a way to pass data through the component tree without having to pass props down manually at every level. It's designed to share data that can be considered "global" for a tree of React components, such as the current authenticated user, theme, or preferred language.

To use Context in React, you need to follow these steps:

  1. Create a Context: First, you need to create a Context object. This is done using React.createContext(defaultValue). The defaultValue argument is only used when a component does not have a matching Provider above it in the tree.

const MyContext = React.createContext(defaultValue);

Enter fullscreen mode Exit fullscreen mode
  1. Provide a Context value: For the context to be accessible to components, it needs to have a Context Provider. This is done using <MyContext.Provider value={/* some value */}>. All components nested inside it will have access to the provided value.

<MyContext.Provider value={/* some value */}>
  {/* children */}
</MyContext.Provider>

Enter fullscreen mode Exit fullscreen mode
  1. Consume the Context value: Any nested component can access the Context value without it being passed explicitly. This is done using the Context Consumer. There are two ways to consume a Context:
  • Using <MyContext.Consumer>:

<MyContext.Consumer>
  {value => /* render something based on the context value */}
</MyContext.Consumer>

Enter fullscreen mode Exit fullscreen mode
  • Using the useContext() hook:

const value = React.useContext(MyContext);

Enter fullscreen mode Exit fullscreen mode

Here's a simple example of using Context:


import React from 'react';

// Create a Context
const NumberContext = React.createContext();

// It returns a Provider and Consumer 
function App() {
  return (
    <NumberContext.Provider value={42}>
      <div>
        <Display />
      </div>
    </NumberContext.Provider>
  );
}

function Display() {
  // Use the Consumer to grab the value from context
  // Notice this component didn't get any props!
  return (
    <NumberContext.Consumer>
      {value => <div>The answer is {value}.</div>}
    </NumberContext.Consumer>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

In this example, the Display component can access the value from the NumberContext without it being passed as a prop. It's getting it directly from the context.

9. How keys work in React? Why are they important?

Keys in React are used to identify unique VDOM elements (the elements in the "virtual DOM", which is a light-weight copy of the actual DOM). They are important for performance reasons when rendering dynamic children, or multiple children in an array or iterator.

When React renders array elements, it needs a way to keep track of each element. If the state changes, React needs to determine what has changed in order to efficiently update and re-render components. Keys help React identify which items have changed, are added, or are removed.

For example, if you have a list of items:


const todoItems = todos.map((todo) =>
  <li>{todo.text}</li>
);

Enter fullscreen mode Exit fullscreen mode

React doesn't have a way to identify each element uniquely. So, you should provide a key attribute:


const todoItems = todos.map((todo) =>
  <li key={todo.id}>{todo.text}</li>
);

Enter fullscreen mode Exit fullscreen mode

Now, each element has a unique key (todo.id in this case), and React can identify it even after state changes.

It's important to note that keys should be stable, predictable, and unique for each element. Using the array index as a key is not recommended if the list can reorder, as this can negatively impact performance and may cause issues with the component state.

10. What are React Fragments?

React Fragments are a common pattern in React for a component to return multiple elements. Fragments let you group a list of children without adding extra nodes to the DOM.

Here's an example of how you can use React Fragments:


import React, { Fragment } from 'react';

function Example() {
  return (
    <Fragment>
      <ChildA />
      <ChildB />
      <ChildC />
    </Fragment>
  )
}

Enter fullscreen mode Exit fullscreen mode

In this example, ChildA, ChildB, and ChildC are sibling components that are being returned by the Example component. Without a Fragment, you would have to wrap them in a parent div or other element, which might mess up your CSS or HTML structure.

You can also use a shorter syntax, but this doesn't support keys or attributes:


import React from 'react';

function Example() {
  return (
    <>
      <ChildA />
      <ChildB />
      <ChildC />
    </>
  )
}

Enter fullscreen mode Exit fullscreen mode

In this case, <> and </> are shorthand for <React.Fragment> and </React.Fragment>.

If you like this blog, you can visit my personal blog sehgaltech for more content.

Top comments (0)