React Interview Questions

author image Hirely
at 08 Jan, 2025

Question: What is the useEffect hook in React?

Answer:

The useEffect hook is one of the most important hooks in React. It allows you to perform side effects in functional components, such as fetching data, updating the DOM, subscribing to events, or cleaning up resources like timers and subscriptions. It is React’s answer to lifecycle methods (componentDidMount, componentDidUpdate, and componentWillUnmount) in class components.

Syntax:

useEffect(() => {
  // Code to run when the component mounts or updates

  return () => {
    // Cleanup function (optional)
  };
}, [dependencies]);
  • First argument (() => { ... }): This is a function that will be executed after the render. It is where you put your side-effect code, such as data fetching, subscriptions, or DOM manipulations.
  • Return value (optional): A cleanup function that will run when the component unmounts or before the effect runs again (for example, to unsubscribe from events or clear timers).
  • Second argument ([dependencies]): This is an optional array of dependencies. The effect will only run when the specified values in the array change. If you pass an empty array [], the effect will only run on mount and unmount (equivalent to componentDidMount and componentWillUnmount in class components). If you omit the array, the effect will run after every render (like componentDidUpdate).

How useEffect Works:

  1. Runs after every render by default: By default, the effect runs after every render of the component, but you can customize this behavior using the dependencies array.

  2. Cleanup: If the effect includes a return statement with a cleanup function, that cleanup function will run before the component unmounts or before the effect is re-executed.

Example 1: Basic Usage of useEffect

Here’s a basic example where we use useEffect to run a side effect when a component mounts:

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

const Example = () => {
  const [count, setCount] = useState(0);

  // Effect that runs after the component mounts or updates
  useEffect(() => {
    console.log('Component has mounted or updated!');
  });

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

export default Example;

In this case, useEffect will run after every render, logging to the console each time the component updates.

Example 2: Running useEffect Only on Mount (Equivalent to componentDidMount)

If you want the effect to run only when the component mounts and not on subsequent updates, you can pass an empty array [] as the second argument:

import React, { useEffect } from 'react';

const Example = () => {
  useEffect(() => {
    console.log('Component has mounted!');
  }, []); // Empty array means this effect only runs on mount

  return <div>Hello, world!</div>;
};

export default Example;

Here, the effect will run only once after the component mounts, mimicking the behavior of componentDidMount in class components.

Example 3: Running useEffect When Dependencies Change (Equivalent to componentDidUpdate)

If you want the effect to run only when certain props or state variables change, pass those dependencies in the second argument:

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

const Example = () => {
  const [count, setCount] = useState(0);

  // Effect that runs only when 'count' changes
  useEffect(() => {
    console.log(`Count has changed to: ${count}`);
  }, [count]); // Effect runs when 'count' changes

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

export default Example;

In this example, the effect will run only when the count state variable changes.

Example 4: Cleanup in useEffect (Equivalent to componentWillUnmount)

Sometimes, you need to clean up resources (e.g., unsubscribing from a service, clearing timers) when the component unmounts or when a dependency changes. You can do this by returning a cleanup function from the useEffect callback.

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

const Example = () => {
  const [count, setCount] = useState(0);

  // Set up a timer and clean it up when the component unmounts or count changes
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('Timer is running');
    }, 1000);

    // Cleanup function to clear the timer
    return () => {
      clearInterval(timer);
      console.log('Timer has been cleared');
    };
  }, [count]); // Runs when 'count' changes

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

export default Example;

Here:

  • A timer is set up when the component is mounted, and the timer is cleared when the component unmounts or when the count state changes.

Key Points about useEffect:

  1. Side Effects: useEffect is used to handle side effects, such as fetching data, directly interacting with the DOM, or subscribing to external services (e.g., WebSocket, event listeners).

  2. Effect Cleanup: If your effect has a side effect that needs cleanup (like timers, event listeners, subscriptions), return a function from the effect callback. This function will be called when the component unmounts or before the effect runs again.

  3. Dependencies: The second argument (dependencies) is crucial in controlling when the effect runs. If you pass an empty array [], the effect will only run on mount and unmount. If you pass specific dependencies, the effect will only run when one of those dependencies changes.

  4. Async Code in useEffect: While useEffect itself cannot return a Promise, you can define an asynchronous function inside the effect and call it:

    useEffect(() => {
      const fetchData = async () => {
        const response = await fetch('https://api.example.com/data');
        const data = await response.json();
        console.log(data);
      };
    
      fetchData();
    }, []);
  5. Multiple Effects: You can use multiple useEffect hooks in the same component to handle different side effects independently.

    useEffect(() => {
      // Effect 1: Fetch data from an API
    }, []);
    
    useEffect(() => {
      // Effect 2: Set up an event listener
    }, [dependency]);

Summary:

The useEffect hook in React allows you to perform side effects in functional components. It runs after the render and can be customized to run only when certain dependencies change or when the component mounts and unmounts. The hook also supports cleanup, making it ideal for managing resources like timers, subscriptions, and fetching data.

By using useEffect, you can create more interactive and dynamic components that perform tasks like data fetching, event handling, and resource management, all while keeping your components declarative and clean.

Question: What are controlled and uncontrolled components in React?

Answer:

In React, controlled and uncontrolled components refer to the way form elements (such as input fields, textareas, and select boxes) handle their state.

Controlled Components:

A controlled component is an input element whose value is controlled by the state of the React component. In other words, the React state is the “single source of truth” for the input’s value. When the value of the input changes, it triggers an event (usually onChange) that updates the state, and the state in turn updates the input’s value.

In a controlled component, the value of the form element is set by a state variable, and changes to the input field are handled through state updates. Every input change will cause the component to re-render, making the form element’s value reflect the latest state.

Example of a Controlled Component:

import React, { useState } from 'react';

const ControlledInput = () => {
  const [inputValue, setInputValue] = useState('');

  // Handle the change in the input field
  const handleChange = (event) => {
    setInputValue(event.target.value);
  };

  return (
    <div>
      <input
        type="text"
        value={inputValue}  // Value of input is controlled by React state
        onChange={handleChange}  // Update state on change
      />
      <p>Entered Text: {inputValue}</p>
    </div>
  );
};

export default ControlledInput;

Key Characteristics of Controlled Components:

  1. Value is controlled by React state: The value of the form element is tied to a state variable.
  2. State updates trigger re-renders: Any change in the form element triggers a state update and a re-render of the component.
  3. Form handling is done via React: All form-related logic (validations, updates, etc.) is handled via React state.
  4. Better control over input values: You can enforce constraints, validation, or custom behaviors directly via React logic.

Uncontrolled Components:

An uncontrolled component is an input element where the form element itself maintains its own internal state. Instead of tying the input value to a React state variable, an uncontrolled component relies on the DOM to manage the value. React does not directly control the form element’s state, but you can still interact with it using a ref.

In an uncontrolled component, the value of the form element is accessed through a ref when needed (for example, during form submission).

Example of an Uncontrolled Component:

import React, { useRef } from 'react';

const UncontrolledInput = () => {
  const inputRef = useRef(null);

  const handleSubmit = (event) => {
    event.preventDefault();
    // Access the value of the input using ref
    alert('Entered Text: ' + inputRef.current.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        ref={inputRef}  // Use ref to interact with the input element
      />
      <button type="submit">Submit</button>
    </form>
  );
};

export default UncontrolledInput;

Key Characteristics of Uncontrolled Components:

  1. Value is not controlled by React state: The form element manages its own state internally.
  2. React doesn’t directly handle form updates: The input element’s state (value) is handled by the DOM, not React.
  3. Access via ref: You can access and interact with the form element’s value via a ref when needed, such as on form submission.
  4. Simpler for certain cases: Uncontrolled components are often easier to set up when you don’t need to track or modify the value while the user is interacting with the input.

Differences between Controlled and Uncontrolled Components:

FeatureControlled ComponentsUncontrolled Components
State managementReact state controls the form element’s valueDOM manages the value of the form element
ValueTied to React state (value attribute)Managed by the DOM (via refs)
Form handlingForm state is updated via React (onChange)Use refs to interact with form values at a later time (e.g., inputRef.current.value)
Re-rendersRe-renders the component on each input changeNo re-render triggered when the input value changes
Use caseBest for cases where form values need to be dynamically controlled or validated by ReactBest for simple forms where React does not need to control the input value continuously
ComplexityMore complex (state management and event handling required)Simpler, with less React overhead
Use of refRarely used (though it can still be used in specific cases)Frequently used to access form values or elements

When to Use Controlled vs. Uncontrolled Components:

  • Controlled Components:

    • When you need to manage the input’s value dynamically.
    • When you need validation, formatting, or other behavior that should depend on the current value.
    • When you want to enforce consistent state across your component (e.g., ensuring that only certain input values are valid).
  • Uncontrolled Components:

    • For simpler forms where you do not need to manage the form’s value actively.
    • When you want to interact with the input only at specific times (e.g., on form submission) rather than on every change.
    • For integrating React into existing non-React codebases or libraries that manage form values independently.

Summary:

  • Controlled components are those whose value is tied to a React state variable. React manages the form element’s value, making it possible to apply validation and other logic.
  • Uncontrolled components are those that store their value in the DOM, and React only interacts with them via refs when needed.

Controlled components offer more flexibility and are the preferred choice in most React applications due to their clear integration with React’s state management system. However, for simple use cases or when performance is a concern (such as in large forms with a lot of inputs), uncontrolled components can be a simpler and more efficient choice.

Read More

If you can’t get enough from this article, Aihirely has plenty more related information, such as ReactJS interview questions, ReactJS interview experiences, and details about various ReactJS job positions. Click here to check it out.

Related Posts

Trace Job opportunities

Hirely, your exclusive interview companion, empowers your competence and facilitates your interviews.

Get Started Now