Basic

12. How do you handle side effects in functional components in React?

Overview

Handling side effects in functional components in React is crucial for managing tasks such as data fetching, subscriptions, or manually changing the DOM from React components. With the introduction of Hooks in React 16.8, functional components can now easily manage side effects, making your code more reusable and easier to follow.

Key Concepts

  1. useEffect Hook: Allows you to perform side effects in functional components.
  2. Cleanup function: Used to clean up resources (like timers or subscriptions) to prevent memory leaks.
  3. Dependency Array: Controls the execution of side effects based on the values it contains.

Common Interview Questions

Basic Level

  1. How can you perform side effects in a React functional component?
  2. What is the purpose of the dependency array in the useEffect Hook?

Intermediate Level

  1. How do you ensure a side effect runs only once in a functional component?

Advanced Level

  1. How can you optimize performance in a component with multiple useEffect hooks?

Detailed Answers

1. How can you perform side effects in a React functional component?

Answer: In React functional components, side effects are handled using the useEffect hook. This hook lets you perform side effects in your components, such as data fetching, subscriptions, or manually altering the DOM.

Key Points:
- useEffect takes two arguments: a function where your side effect code is placed, and an optional dependency array.
- It runs after every render by default, including the first render.
- Cleanup can be performed by returning a function from the effect.

Example:

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

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

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

2. What is the purpose of the dependency array in the useEffect Hook?

Answer: The dependency array in the useEffect Hook is a way to optimize the execution of effects by specifying when the effect should re-run. If the values in the dependency array stay the same between renderings, the effect will not re-run.

Key Points:
- An empty array [] makes the effect run only on mount and clean up on unmount.
- Including values in the array causes the effect to re-run if those values change.
- Omitting the dependency array causes the effect to run after every rendering.

Example:

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

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

  // Only re-run the effect if count changes
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]); // Dependency array

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

3. How do you ensure a side effect runs only once in a functional component?

Answer: To ensure a side effect runs only once in a functional component, you can pass an empty dependency array [] to the useEffect hook. This tells React that the effect doesn’t depend on any values from props or state, so it never needs to re-run.

Key Points:
- This is similar to the componentDidMount lifecycle method in class components.
- It's especially useful for fetching data or setting up a subscription.

Example:

import React, { useEffect } from 'react';

function Example() {
  useEffect(() => {
    // Fetch data from an API and log it
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => console.log(data));

  }, []); // Empty dependency array means this runs once after the initial render

  return (
    <div>Check the console for data.</div>
  );
}

4. How can you optimize performance in a component with multiple useEffect hooks?

Answer: Optimizing performance in a component with multiple useEffect hooks involves isolating effects by their dependencies and utilizing the dependency array effectively. Each effect should only listen to the state or props it needs to, thereby minimizing unnecessary re-renders or effect executions.

Key Points:
- Separate effects into different useEffect calls based on their logic and dependencies.
- Ensure the dependency array accurately reflects all variables used within the effect.
- Consider combining related logic into a single effect if they share dependencies to minimize the number of effect executions.

Example:

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

function Example() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('John');

  // Effect for updating the document title
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]); // Only re-run if count changes

  // Separate effect for fetching user data
  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch(`https://api.example.com/user/${name}`);
      const data = await response.json();
      console.log(data);
    };

    fetchData();
  }, [name]); // Only re-run if name changes

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <input value={name} onChange={e => setName(e.target.value)} />
    </div>
  );
}

In this guide, JavaScript examples are used to demonstrate React concepts due to the nature of React development. The use of C# code examples is not applicable to React interview questions.