DEV Community

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

Posted on

Most Commonly Asked React Interview Questions 2024 - Part 3

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

1. How do you handle error boundaries in React?

Error boundaries in React are a way to catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.

Here's an example of how you can define an error boundary:


class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

Enter fullscreen mode Exit fullscreen mode

You can use it as a regular component:


<ErrorBoundary>
  <MyComponent />
</ErrorBoundary>

Enter fullscreen mode Exit fullscreen mode

In the above example, if MyComponent or any of its children throw an error during rendering, the error will be caught by the ErrorBoundary, the fallback UI will be rendered, and the error will be logged.

Please note that error boundaries only catch errors in the components below them in the tree. An error boundary can’t catch an error within itself.

2. What is the use of refs in React?

Refs in React are used to access and interact with DOM nodes or React elements directly. They are used in cases where you need to imperatively modify a child outside of the typical data flow. Here are a few common use cases for refs:

  1. Managing focus, text selection, or media playback.
  2. Triggering imperative animations.
  3. Integrating with third-party DOM libraries.

In functional components, refs are used with the useRef hook. Here's an example of how to use refs to manage focus on an input element in a functional component:


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

function MyComponent() {
  const myRef = useRef(null);

  useEffect(() => {
    myRef.current.focus();
  }, []);

  return <input ref={myRef} />;
}

Enter fullscreen mode Exit fullscreen mode

In this example, myRef is a reference to the DOM node rendered by the <input /> element, so it can be used to access the properties and methods of this node.

It's important to note that you should avoid using refs for anything that can be done declaratively. For example, instead of using a ref to expose text content of a <span>, you should pass it as a prop.

3. How do you manage side effects in React?

In React, side effects are managed using a built-in hook called useEffect. Side effects are operations that can affect other components and can't be done during rendering. These include data fetching, subscriptions, or manually changing the DOM.

The useEffect hook accepts two arguments: a function where you can perform your side effects, and an array of dependencies. The function will run after the render is committed to the screen.

If you pass an empty array ([]) as the second argument, the side effect runs once after the initial render, similar to componentDidMount in class components. If you pass variables in the array, the side effect will run whenever any of those variables change, similar to componentDidUpdate.

Here's an example of using useEffect to fetch data from an API:


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

function MyComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data));
  }, []); // Empty array means this effect runs once after initial render

  return (
    <div>
      {data ? `Data: ${data}` : 'Loading...'}
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

In this example, the useEffect hook fetches data from an API and updates the component's state with the fetched data. The empty array ([]) means the data fetching happens once after the component mounts.

4. What is the difference between a controlled component and an uncontrolled component?

In React, the terms "controlled" and "uncontrolled" components refer to the way data is managed in form elements.

  1. Controlled Components: Controlled components are components where form data is controlled by React state. Any changes to the input field are handled by functions within the component, which means that the form data is directly controlled by the React component. The value of the input field is always driven by the state in the component. Here's an example:

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
  1. Uncontrolled Components: Uncontrolled components are components where form data is handled by the DOM itself rather than React state. Instead of writing an event handler for every state update, you can use a ref to get form values from the DOM. The value of the input field is thus controlled by the DOM itself, not by the React component. Here's an example:

import React from 'react';

class MyForm extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

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

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

export default MyForm;

Enter fullscreen mode Exit fullscreen mode

In general, it's recommended to use controlled components because they allow for more predictable code and are more in line with the "React way" of doing things, which is to keep state in the React component's state and render it declaratively. However, uncontrolled components can be useful for some types of inputs, like file uploads, or for integrating with non-React code.

5. How do you test React components?

Testing React components can be done using several libraries and tools. Here are some of the most commonly used ones:

  1. Jest: Jest is a JavaScript testing framework developed by Facebook. It's great for testing JavaScript and React code. It works out of the box with minimal configuration and has features like a complete and easy-to-use API, mocking support, and a powerful matcher library.

  2. React Testing Library: This is a very light-weight solution for testing React components. It provides light utility functions on top of react-dom and react-dom/test-utils, in a way that encourages better testing practices. Its primary guiding principle is: "The more your tests resemble the way your software is used, the more confidence they can give you."

  3. Enzyme: Enzyme is a JavaScript Testing utility for React that makes it easier to test your React Components' output. You can also manipulate, traverse, and in some ways simulate runtime given the output. Enzyme's API is meant to be intuitive and flexible by mimicking jQuery's API for DOM manipulation and traversal.

Here's an example of how you might test a simple React component using Jest and React Testing Library:


import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Button from '../Button'; // This is the component to be tested

test('Check button click updates the count', () => {
  const { getByText } = render(<Button />);

  // Button initially starts with "Times clicked: 0"
  expect(getByText('Times clicked: 0')).toBeInTheDocument();

  // Simulate a button click
  fireEvent.click(getByText('Click me'));

  // Expect the button text to have changed after click
  expect(getByText('Times clicked: 1')).toBeInTheDocument();
});

Enter fullscreen mode Exit fullscreen mode

In this example, we're rendering the Button component and then checking that the initial button text is as expected. We then simulate a button click and check that the button text updates as expected.

Remember, testing is a vast topic and the approach you take will depend on many factors such as the complexity of the component, whether it's a class or functional component, whether it uses state or props, etc.

6. What is prop drilling in React?

Prop drilling is a term used in the React community to refer to the process of getting data to parts of the React Component tree. This involves passing props down from a parent component to its child components, and then further down to their children, and so on. This can become particularly problematic when the tree depth increases, as components in between that don't need the data still need to be passed the props to provide it to components deeper in the tree.

This is not an issue with small applications or components, but it can become cumbersome in larger applications. It can lead to code that is hard to maintain and understand. To avoid prop drilling, you can use state management solutions like Redux, MobX or React's built-in Context API. These solutions allow you to manage and access state from any component without having to pass props down through intermediate components.

7. What are Custom Hooks?

Custom Hooks are a feature in React that allow you to create your own hooks to reuse stateful behavior between different components. Custom Hooks are JavaScript functions whose names are prefixed with the word 'use'. A custom Hook is a normal function which can use other Hooks inside of it.

The purpose of custom Hooks is to allow you to use component logic into reusable functions. For example, you might want to use the same data fetching logic in multiple components. With custom Hooks, you can extract this logic into a reusable function.

Here's an example of a custom Hook:


import { useState, useEffect } from 'react';

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

  useEffect(() => {
    async function fetchData() {
      const response = await fetch(url);
      const data = await response.json();
      setData(data);
      setLoading(false);
    }
    fetchData();
  }, [url]);

  return { data, loading };
}

export default useFetch;

Enter fullscreen mode Exit fullscreen mode

In this example, useFetch is a custom Hook that fetches data from a URL. It uses the useState and useEffect Hooks internally. You can use it in your components like this:


import React from 'react';
import useFetch from './useFetch';

function MyComponent() {
  const { data, loading } = useFetch('https://api.example.com/data');

  if (loading) {
    return 'Loading...';
  }

  return <div>Data: {data}</div>;
}

Enter fullscreen mode Exit fullscreen mode

In this component, we're using the useFetch custom Hook to fetch data from an API. The useFetch Hook returns an object with data and loading properties, which we can use in our component to render different content based on whether the data is still loading.

8. Explain Strict Mode in React.

StrictMode is a tool for highlighting potential problems in an application. It does not render any visible UI. It activates additional checks and warnings for its descendants.

StrictMode currently helps with:

  • Identifying components with unsafe lifecycles
  • Warning about legacy string ref API usage
  • Warning about deprecated findDOMNode usage
  • Detecting unexpected side effects
  • Detecting legacy context API

To use the StrictMode, you just need to wrap your component with it. Here is an example:


import React from 'react';

function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode>
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>
      <Footer />
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

In the above example, the StrictMode checks will apply to ComponentOne and ComponentTwo components but not Header and Footer components.

Please note that StrictMode checks are only run in development mode; they do not impact the production build.

9. How to prevent re-renders in React?

There are several ways to prevent unnecessary re-renders in React:

  1. ShouldComponentUpdate: This lifecycle method can be used in class components. It allows you to control whether a component should re-render or not by returning a boolean value. If shouldComponentUpdate returns false, then the component will not re-render.

shouldComponentUpdate(nextProps, nextState) {
  // compare with current props and state
  return nextProps.id !== this.props.id;
}

Enter fullscreen mode Exit fullscreen mode
  1. React.PureComponent: PureComponent is similar to Component, but it handles the shouldComponentUpdate method for you. When props or state changes, PureComponent will do a shallow comparison on both props and state. If nothing has changed, it will not trigger a re-render.

class MyComponent extends React.PureComponent {
  // your component code
}

Enter fullscreen mode Exit fullscreen mode
  1. React.memo: React.memo is a higher order component. It's similar to React.PureComponent but for function components instead of classes. React.memo will do a shallow comparison of props and prevent re-render if props haven't changed.

const MyComponent = React.memo(function MyComponent(props) {
  // your component code
});

Enter fullscreen mode Exit fullscreen mode
  1. useMemo Hook: The useMemo hook allows you to prevent expensive calculations on every render. It returns a memoized version of the function that only re-computes the result when one of the dependencies has changed.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

Enter fullscreen mode Exit fullscreen mode
  1. useCallback Hook: The useCallback hook returns a memoized version of the callback function that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

Enter fullscreen mode Exit fullscreen mode

Remember, preventing re-renders can optimize your app, but it also adds complexity. So, use these techniques wisely and only when necessary.

10. How to re-render the view when the browser is resized?

In React, you can use the window object's resize event to trigger a re-render when the browser is resized. However, directly manipulating the DOM or using events like this is not the React way of doing things. Instead, you can use the useState and useEffect hooks to create a custom hook that listens for resize events. Here's an example:


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

// Custom hook
function useWindowSize() {
  const [windowSize, setWindowSize] = useState({
    width: undefined,
    height: undefined,
  });

  useEffect(() => {
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }

    window.addEventListener('resize', handleResize);

    // Call handler right away so state gets updated with initial window size
    handleResize();

    // Remove event listener on cleanup
    return () => window.removeEventListener('resize', handleResize);
  }, []); // Empty array ensures that effect is only run on mount and unmount

  return windowSize;
}

function ExampleComponent() {
  const size = useWindowSize();

  return (
    <div>
      {size.width}px / {size.height}px
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

In this example, useWindowSize is a custom hook that listens for the resize event on the window object. When the window is resized, it updates the state, which causes a re-render of any component using this hook. The ExampleComponent uses this hook and will re-render whenever the window size changes.

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

Top comments (0)