60 New React Hooks Interview Question

Table of Contents

Introduction

React Hooks have revolutionized the way we write React components by providing a more elegant and concise way of managing state and side effects. If you’re preparing for a React Hooks interview, you can expect questions that test your understanding of these powerful features. Some common interview questions may include explaining what React Hooks are, discussing the benefits they offer, describing the useState and useEffect hooks, discussing the rules of using hooks correctly, and explaining the difference between class components and functional components with hooks. Understanding these concepts will help you showcase your proficiency in React Hooks during your interview.

Basic Questions

1. What are React Hooks?

React Hooks are functions that allow you to use state and other React features in functional components, which were traditionally reserved for class components. They were introduced in React 16.8 to provide a more concise and reusable way of managing state and side-effects within functional components.

Example:

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

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

  const increment = () => {
    setCount(count + 1);
  };

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

2. What problems do React Hooks solve?

React Hooks solve the following problems:

  • Managing state and lifecycle in functional components without the need for class components.
  • Avoiding complex patterns like Higher-Order Components (HOCs) and Render Props for sharing logic.
  • Improving code readability and reusability.

Example:
Consider a scenario where you have two components that need to share the same state logic.

Without Hooks:

JSX
// Using a Higher-Order Component (HOC)
const withCounter = (WrappedComponent) => {
  class WithCounter extends React.Component {
    state = {
      count: 0,
    };

    increment = () => {
      this.setState((prevState) => ({
        count: prevState.count + 1,
      }));
    };

    render() {
      return (
        <WrappedComponent
          count={this.state.count}
          increment={this.increment}
          {...this.props}
        />
      );
    }
  }

  return WithCounter;
};

const DisplayCounter = ({ count, increment }) => {
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

const CounterWithLogic = withCounter(DisplayCounter);

With Hooks:

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

  const increment = () => {
    setCount(count + 1);
  };

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

As you can see, Hooks provide a more straightforward and cleaner approach to sharing logic between components.

3. What is the useState hook in React?

The useState hook is a built-in React hook that allows functional components to add state to their components.

Example:

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

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

  const increment = () => {
    setCount(count + 1);
  };

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

In this example, useState is used to add state to the Counter component. It returns an array with the current state value (count) and a function (setCount) to update the state. The initial state value is provided as an argument to useState (0 in this case).

4. What is the useEffect hook in React?

The useEffect hook is a built-in React hook that allows you to perform side effects in functional components. It combines the functionality of componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods from class components.

Example:

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

const DataFetcher = () => {
  const [data, setData] = useState([]);

  useEffect(() => {
    // Simulating an API call or any side effect that fetches data
    const fetchData = async () => {
      const response = await fetch('https://api.example.com/data');
      const jsonData = await response.json();
      setData(jsonData);
    };

    fetchData();
  }, []); // Empty dependency array means this effect runs only once on component mount

  return (
    <div>
      <h2>Fetched Data:</h2>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
};

In this example, useEffect is used to fetch data when the component mounts. The empty dependency array ([]) as the second argument means the effect runs only once when the component mounts. If you provide a variable inside the dependency array, the effect will run whenever that variable changes.

5. Can you explain the rules of hooks in React?

The rules of hooks in React are:

  1. Only Call Hooks at the Top Level: You should not call hooks inside loops, conditions, or nested functions. Hooks should be called directly within the functional component body or other custom hooks.
  2. Call Hooks from React Functions: Hooks can only be called from React functional components or custom hooks. Don’t call hooks from regular JavaScript functions.
  3. Don’t Call Hooks Conditionally: Hooks should always be called unconditionally, not inside conditional statements. This ensures that the order of hooks remains consistent across renders.
  4. Use the Same Order in Each Render: Make sure to use hooks in the same order for each render. Don’t introduce conditional rendering that causes hooks to be called in a different order, as this can lead to bugs.
  5. Use Hooks with React Components Only: Custom hooks are allowed to use other hooks, but regular JavaScript functions should not use hooks.

6. What are custom hooks in React? Can you give an example?

Custom hooks in React are JavaScript functions that use one or more built-in hooks and possibly other custom hooks to encapsulate and share logic between different components.

Example:

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

const useDataFetcher = (url) => {
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch(url);
      const jsonData = await response.json();
      setData(jsonData);
    };

    fetchData();
  }, [url]);

  return data;
};

// Usage
const DataFetcher = () => {
  const data = useDataFetcher('https://api.example.com/data');

  return (
    <div>
      <h2>Fetched Data:</h2>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
};

In this example, useDataFetcher is a custom hook that encapsulates the data-fetching logic using the useEffect and useState hooks. It takes a URL as an argument and returns the fetched data, allowing any component to reuse this logic by calling useDataFetcher.

7. What is the useReducer hook in React?

The useReducer hook is a built-in React hook used for managing complex state logic that involves multiple sub-values or when the next state depends on the previous state.

Example:

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

const initialState = { count: 0 };

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

const CounterWithReducer = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
};

In this example, useReducer is used to manage the state of the count in a more complex way than using useState. The reducer function takes the current state and an action, and based on the action type, it returns the updated state.

8. What is the useContext hook in React?

The useContext hook is a built-in React hook that allows you to consume data from a Context Provider within a functional component.

Example:

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

const UserContext = createContext();

const UserProfile = () => {
  const user = useContext(UserContext);

  return (
    <div>
      <h2>User Profile</h2>
      <p>Name: {user.name}</p>
      <p>Email: {user.email}</p>
    </div>
  );
};

const App = () => {
  const user = { name: 'John Doe', email: 'john@example.com' };

  return (
    <UserContext.Provider value={user}>
      <UserProfile />
    </UserContext.Provider>
  );
};

In this example, useContext is used to consume the user data from the UserContext Provider in the UserProfile component. The UserContext.Provider in the App component provides the user data to all components within its tree.

9. How does the useRef hook work?

The useRef hook is a built-in React hook that allows you to create a mutable object that persists across renders.

Example:

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

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

  useEffect(() => {
    // Focus the input element on component mount
    inputRef.current.focus();
  }, []);

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
    </div>
  );
};

In this example, useRef is used to create a reference to the input element. The useEffect hook is used to focus the input element when the component mounts. The inputRef object allows us to interact with the DOM element directly.

10. Explain how the useCallback hook works.

The useCallback hook is a built-in React hook that is used to memoize functions, preventing unnecessary re-creation of functions on each render.

Example:

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

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

  const increment = useCallback(() => {
    setCount((prevCount) => prevCount + 1);
  }, []);

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

In this example, the increment function is memoized using useCallback. The empty dependency array ([]) means that the function is created only once when the component mounts and will not change on subsequent renders. This is useful when passing functions to child components, as it ensures they do not unnecessarily re-render if their dependencies haven’t changed.

11. What are the differences between class components and hooks in React?

PropertyClass ComponentsReact Hooks
StateUse this.state and this.setState() to manage state.Use useState hook to manage state.
LifecycleUse lifecycle methods like componentDidMount, componentDidUpdate, etc.Use useEffect hook to perform side effects and mimic lifecycle behavior.
Logic SharingUse Higher-Order Components (HOCs) or Render Props to share logic between components.Use Custom Hooks to share logic between components.
ReadabilityCan lead to larger and harder-to-read code.Can lead to more concise and readable code.
Learning CurveSteeper learning curve for newcomers.Easier for newcomers to pick up functional approach with hooks.
Class BindingRequires explicit binding of class methods.No need for binding with hooks.

12. What is the useMemo hook in React?

The useMemo hook is a built-in React hook that is used to memoize a value and prevent unnecessary recalculations of that value on each render.

Example:

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

const FibonacciCalculator = () => {
  const [n, setN] = useState(1);

  const fibonacciValue = useMemo(() => {
    const fibonacci = (num) => {
      if (num <= 1) return num;
      return fibonacci(num - 1) + fibonacci(num - 2);
    };
    return fibonacci(n);
  }, [n]);

  return (
    <div>
      <p>Calculate Fibonacci of: {n}</p>
      <input type="number" value={n} onChange={(e) => setN(parseInt(e.target.value))} />
      <p>Fibonacci Value: {fibonacciValue}</p>
    </div>
  );
};

In this example, the fibonacciValue is memoized using useMemo. It will only be recalculated when the n variable (the input value) changes.

13. How do you implement a lifecycle method with hooks?

You can implement a lifecycle method equivalent using the useEffect hook.

Example – Equivalent of componentDidMount:

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

const MyComponent = () => {
  useEffect(() => {
    // This function runs on component mount (equivalent to componentDidMount)
    console.log('Component is mounted.');

    // Return a cleanup function (equivalent to componentWillUnmount)
    return () => {
      console.log('Component will unmount.');
    };
  }, []);

  return <div>My Component</div>;
};

In this example, the function passed to useEffect will run when the component mounts. The empty dependency array ([]) ensures that the effect only runs once when the component is mounted, simulating the behavior of componentDidMount. If you need to simulate componentDidUpdate, you can pass a dependency array with the variables that you want to watch for changes.

14. What is the purpose of the useLayoutEffect hook?

The useLayoutEffect hook is similar to useEffect, but it runs synchronously after all DOM mutations. This makes it suitable for tasks that require measurements or updates based on DOM size and position.

Example:

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

const ResizeAwareComponent = () => {
  const [width, setWidth] = useState(0);
  const [height, setHeight] = useState(0);

  useLayoutEffect(() => {
    // This runs after DOM mutations but before the screen is repainted
    const handleResize = () => {
      setWidth(window.innerWidth);
      setHeight(window.innerHeight);
    };

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return (
    <div>
      <p>Window Width: {width}</p>
      <p>Window Height: {height}</p>
    </div>
  );
};

In this example, useLayoutEffect is used to update the width and height state variables after DOM mutations (e.g., when the window is resized). This ensures that the state variables are updated synchronously before the screen repaints.

15. When would you use the useImperativeHandle hook?

The useImperativeHandle hook allows a parent component to gain access to specific functions or data of a child component when using forwardRef. It’s used in specific cases when you want to expose a child component’s methods to its parent.

Example:

JSX
import React, { useRef, useImperativeHandle, forwardRef } from 'react';

const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focusInput: () => {
      inputRef.current.focus();
    },
    getValue: () => {
      return inputRef.current.value;
    },
  }));

  return <input ref={inputRef} type="text" />;
});

const ParentComponent = () => {
  const childRef = useRef();

  const handleButtonClick = () => {
    childRef.current.focusInput();
    const value = childRef.current.getValue();
    console.log('Value:', value);
  };

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={handleButtonClick}>Get Value</button>
    </div>
  );
};

In this example, useImperativeHandle is used to expose two functions, focusInput and getValue, from the ChildComponent to the ParentComponent. The ParentComponent can then use these functions through the childRef to interact with the input element inside ChildComponent.

16. What is the purpose of the useDebugValue hook?

The useDebugValue hook is used to display custom labels for custom hooks in React DevTools.

Example:

JSX
import { useState, useEffect, useDebugValue } from 'react';

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

  useEffect(() => {
    // Some side-effect
    document.title = `Count: ${count}`;
  }, [count]);

  useDebugValue(`Custom Hook Count: ${count}`);

  return count;
};

const ComponentUsingCustomHook = () => {
  const count = useCustomHook();

  return (
    <div>
      <p>Count: {count}</p>
    </div>
  );
};

In this example, useDebugValue is used to provide a custom label for the custom hook useCustomHook. When you inspect the ComponentUsingCustomHook in React DevTools, it will display the custom label Custom Hook Count: {count}.

17. What is the difference between useEffect and useLayoutEffect?

The primary difference between useEffect and useLayoutEffect is the timing of their execution:

  • useEffect: Runs asynchronously after the component renders and the browser has painted.
  • useLayoutEffect: Runs synchronously after the component renders but before the browser repaints.

Example:

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

const ComponentWithEffects = () => {
  const [width, setWidth] = useState(0);
  const [height, setHeight] = useState(0);

  useEffect(() => {
    // This runs asynchronously after the component renders and the browser repaints
    const handleResize = () => {
      setWidth(window.innerWidth);
      setHeight(window.innerHeight);
    };

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  useLayoutEffect(() => {
    // This runs synchronously after the component renders but before the browser repaints
    console.log('Layout effect ran');
  }, []);

  return (
    <div>
      <p>Window Width: {width}</p>
      <p>Window Height: {height}</p>
    </div>
  );
};

In this example, useEffect is used to handle window resizing and update the width and height state variables asynchronously after the browser repaints. useLayoutEffect, on the other hand, is used to log a message synchronously after the component renders but before the browser repaints. This means that the useLayoutEffect code will run before the screen is updated.

18. Can you explain how to fetch data with the useEffect hook?

You can fetch data using the useEffect hook by making an asynchronous call inside the effect function.

Example:

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

const DataFetcher = () => {
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/data');
        const jsonData = await response.json();
        setData(jsonData);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchData();
  }, []); // Empty dependency array means this effect runs only once on component mount

  return (
    <div>
      <h2>Fetched Data:</h2>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
};

In this example, the useEffect hook is used to fetch data from the API using fetch. The data is then stored in the data state

variable, and the component updates accordingly.

19. What does the empty array mean as a second argument in the useEffect hook?

When you provide an empty array ([]) as the second argument to the useEffect hook, it means that the effect will run only once, similar to the behavior of componentDidMount in class components.

Example:

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

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

  useEffect(() => {
    console.log('Component is mounted (useEffect ran).');
  }, []); // Empty dependency array means this effect runs only once on component mount

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

In this example, the useEffect hook has an empty dependency array ([]), which means it will only run once when the component mounts. The message “Component is mounted (useEffect ran).” will be logged to the console only once when the component is initially rendered.

20. What is the behavior of the useEffect hook with no dependencies?

When you provide no dependency array to the useEffect hook (i.e., no second argument), the effect will run on every render, similar to the behavior of componentDidUpdate in class components.

Example:

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

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

  useEffect(() => {
    console.log('useEffect ran on every render.');
  });

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

In this example, the useEffect hook has no dependency array, so it will run on every render of the component. This means that the message “useEffect ran on every render.” will be logged to the console every time the component updates, including when the count state changes due to the button click.

Intermediate Questions

1. How can you mimic componentDidMount with React Hooks?

With React Hooks, you can mimic the componentDidMount lifecycle method by using the useEffect hook with an empty dependency array ([]). This will ensure that the effect runs only once when the component mounts.

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

function MyComponent() {
  useEffect(() => {
    // Mimic componentDidMount logic here
    console.log('Component mounted');

    // Clean up the effect (mimic componentWillUnmount)
    return () => {
      console.log('Component unmounted');
    };
  }, []);

  return <div>My Component</div>;
}

2. Can you explain how to create a custom hook in React?

Custom hooks are functions that use built-in hooks or other custom hooks to share logic between components. To create a custom hook, follow this pattern:

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

function useCustomHook(initialValue) {
  const [state, setState] = useState(initialValue);

  useEffect(() => {
    // Custom logic using state and/or other hooks
    console.log('Custom hook logic');

    // Clean up the effect if needed
    return () => {
      console.log('Clean up custom hook');
    };
  }, [state]); // Add dependencies as needed

  // Return any data or functions needed by the component
  return [state, setState];
}

Then, you can use this custom hook in your components:

JSX
function MyComponent() {
  const [count, setCount] = useCustomHook(0);

  // Other component logic

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

3. How can you handle side effects using useEffect in React?

You can handle side effects using the useEffect hook by providing a callback function that performs the side effect. The useEffect hook allows you to perform actions such as data fetching, subscriptions, or manually changing the DOM.

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

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

  useEffect(() => {
    // Side effect: fetch data from an API
    fetch('https://example.com/api/data')
      .then((response) => response.json())
      .then((data) => setData(data))
      .catch((error) => console.error(error));

    // Clean up the effect if needed (e.g., cancelling subscriptions)
    return () => {
      console.log('Clean up side effect');
    };
  }, []); // Empty dependency array means the effect runs only once (on mount)

  // Render component using fetched data
  return (
    <div>
      {data.map((item) => (
        <p key={item.id}>{item.name}</p>
      ))}
    </div>
  );
}

4. What is the difference between useState and useReducer? When would you use each one?

Both useState and useReducer are used to manage state in a React component, but they have some differences in usage:

  • useState: It is a simpler and more straightforward hook that is suitable for managing individual or independent pieces of state. It takes an initial value and returns the current state and a function to update that state. It’s best used when you have simple state updates.
JSX
import React, { useState } from 'react';

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

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <p>Count: {count}</p>
    </div>
  );
}
  • useReducer: It is a more powerful hook that is suitable for managing complex state logic or when multiple state transitions are closely related. It follows the same principles as the Redux reducer, where you define a reducer function that specifies how the state should change based on dispatched actions.
JSX
import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
      <p>Count: {state.count}</p>
    </div>
  );
}

Use useState when the state management is simple, and you don’t need to handle complex state transitions. Use useReducer when you have more complex state updates or when you need to handle multiple state changes together.

5. How can you handle errors within a useEffect hook?

To handle errors within a useEffect hook, you can use a try-catch block inside the callback function. If an error occurs during the execution of the effect, it will be caught in the catch block, and you can handle it accordingly.

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

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

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch('https://example.com/api/data');
        if (!response.ok) {
          throw new Error('Failed to fetch data');
        }
        const data = await response.json();
        setData(data);
      } catch (error) {
        setError(error.message);
      }
    }

    fetchData();

    return () => {
      // Cleanup if needed
    };
  }, []);

  return (
    <div>
      {error ? (
        <p>Error: {error}</p>
      ) : (
        data.map((item) => <p key={item.id}>{item.name}</p>)
      )}
    </div>
  );
}

6. How would you implement a shouldComponentUpdate lifecycle method with hooks?

With hooks, you can use the React.memo higher-order component to mimic the behavior of shouldComponentUpdate. React.memo prevents unnecessary re-renders by memoizing the result of the component and only re-rendering if the props have changed.

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

function MyComponent({ propA, propB }) {
  // Component's state and logic
  const [count, setCount] = useState(0);
  // ...

  return (
    <div>
      <p>PropA: {propA}</p>
      <p>PropB: {propB}</p>
      <p>Count:

 {count}</p>
      {/* Other JSX */}
    </div>
  );
}

export default React.memo(MyComponent);

With React.memo, the component will only re-render if propA, propB, or any other non-state props change. If the state inside the component changes but the props remain the same, it won’t trigger a re-render.

7. How do you ensure a function does not re-render with React Hooks?

To prevent a function from re-rendering when the component re-renders, you can use the useCallback hook. useCallback returns a memoized version of the function that only changes if one of its dependencies has changed.

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

function MyComponent() {
  const handleClick = useCallback(() => {
    // Function logic
    console.log('Button clicked');
  }, []); // Empty dependency array means the function won't change

  return (
    <div>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

By passing an empty array as the second argument to useCallback, the function will be created only once, and it won’t change on subsequent re-renders unless one of the dependencies in the array changes.

8. How do you share logic between components using hooks?

To share logic between components, you can create a custom hook, as mentioned in question 2. Custom hooks encapsulate reusable logic, and you can use them in multiple components.

JSX
// CustomHook.js
import { useState } from 'react';

function useCustomHook(initialValue) {
  const [state, setState] = useState(initialValue);

  const increment = () => {
    setState((prev) => prev + 1);
  };

  return [state, increment];
}

export default useCustomHook;

Now, you can use this custom hook in different components:

JSX
// ComponentA.js
import React from 'react';
import useCustomHook from './CustomHook';

function ComponentA() {
  const [count, increment] = useCustomHook(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}
JSX
// ComponentB.js
import React from 'react';
import useCustomHook from './CustomHook';

function ComponentB() {
  const [count, increment] = useCustomHook(100);

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

Both ComponentA and ComponentB share the same logic and state management provided by the useCustomHook custom hook.

9. Can you illustrate a situation where you might use the useContext hook?

useContext is used to share data that is considered global or needs to be accessed by multiple components without passing it through props at each level. A typical example is managing user authentication state.

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

// Create a context
const AuthContext = createContext();

// Create a provider (usually at the top-level of the component tree)
function AuthProvider({ children }) {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  return (
    <AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn }}>
      {children}
    </AuthContext.Provider>
  );
}

// Use the context in child components
function LoginComponent() {
  const { isLoggedIn, setIsLoggedIn } = useContext(AuthContext);

  const handleLogin = () => {
    // Perform login logic, e.g., validating user credentials
    setIsLoggedIn(true);
  };

  return (
    <div>
      {isLoggedIn ? (
        <p>Welcome, user!</p>
      ) : (
        <button onClick={handleLogin}>Login</button>
      )}
    </div>
  );
}

function App() {
  return (
    <AuthProvider>
      <LoginComponent />
    </AuthProvider>
  );
}

In this example, the AuthProvider component wraps the entire application, providing the AuthContext. LoginComponent can access the isLoggedIn state and setIsLoggedIn function directly from the context without passing them through props. When the user logs in, the state is managed at the top level and propagated down to all components using useContext.

10. How can we use the useMemo hook to optimize computation-intensive calculations?

The useMemo hook allows you to memoize the result of a computation and avoid unnecessary re-calculations when the dependencies remain the same. This is useful when dealing with computationally expensive operations.

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

function MyComponent({ data }) {
  // Compute an expensive calculation only when 'data' changes
  const result = useMemo(() => {
    console.log('Computing result...');
    // Perform the expensive calculation here
    return data.reduce((acc, curr) => acc + curr, 0);
  }, [data]);

  return <div>Result: {result}</div>;
}

With useMemo, the expensive calculation will only be performed when the data prop changes. On subsequent re-renders with the same data, the memoized result will be returned, and the expensive calculation won’t run again.

11. What does the dispatch function do in the useReducer hook?

In the useReducer hook, the dispatch function is used to dispatch actions to the reducer function. Actions are objects that describe what type of state change should happen. The reducer function takes the current state and the action and returns the new state based on the action type.

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

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
}

The dispatch function is called with an action object containing the type property, which the reducer uses to determine how the state should change. The reducer function then returns the new state, and the component re-renders with the updated state.

12. How can you persist the state of a component using React Hooks between different sessions?

To persist the state of a component between different sessions, you can use the localStorage or sessionStorage APIs in combination with the useState and useEffect hooks.

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

function MyComponent() {
  const [count, setCount]

 = useState(() => {
    const savedCount = localStorage.getItem('count');
    return savedCount ? parseInt(savedCount, 10) : 0;
  });

  useEffect(() => {
    localStorage.setItem('count', count.toString());
  }, [count]);

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

In this example, the initial state of count is obtained from localStorage. Whenever count changes, the useEffect hook updates the value in localStorage, ensuring that the state is persisted between different sessions.

13. How would you implement a timer or interval with React Hooks?

To implement a timer or interval with React Hooks, you can use the useEffect hook to set up and manage the interval.

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

function TimerComponent() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setSeconds((prevSeconds) => prevSeconds + 1);
    }, 1000);

    // Clean up the interval on component unmount
    return () => clearInterval(intervalId);
  }, []);

  return (
    <div>
      <p>Seconds: {seconds}</p>
    </div>
  );
}

In this example, the useEffect hook sets up an interval that increments the seconds state every second (1000 milliseconds). The interval is cleared when the component unmounts to avoid memory leaks.

14. Can you explain the process of testing a custom hook in React?

Testing a custom hook involves writing test cases to verify its behavior. To test a custom hook, you can use testing libraries like Jest and React Testing Library.

Let’s say we have a custom hook that manages a simple counter:

JSX
// useCounter.js
import { useState } from 'react';

function useCounter(initialValue) {
  const [count, setCount] = useState(initialValue);

  const increment = () => {
    setCount((prev) => prev + 1);
  };

  return [count, increment];
}

Now, let’s write some test cases using Jest and React Testing Library:

JSX
// useCounter.test.js
import { renderHook, act } from '@testing-library/react-hooks';
import useCounter from './useCounter';

test('useCounter should increment the count', () => {
  const { result } = renderHook(() => useCounter(0));

  expect(result.current[0]).toBe(0);
  act(() => {
    result.current[1](); // Increment
  });
  expect(result.current[0]).toBe(1);
});

test('useCounter should increment the count by a specified value', () => {
  const { result } = renderHook(() => useCounter(0));

  expect(result.current[0]).toBe(0);
  act(() => {
    result.current[1]();
    result.current[1]();
    result.current[1]();
  });
  expect(result.current[0]).toBe(3);
});

In these test cases, we use renderHook from React Testing Library to render the custom hook and get the result object that contains the current state and function returned by the hook. We use act to interact with the hook, triggering updates, and then assert the expected values.

15. How do you deal with event listeners in useEffect?

When using event listeners in the useEffect hook, it’s important to add and remove the event listeners correctly to avoid memory leaks and multiple event listeners being attached.

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

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

  useEffect(() => {
    // Add event listener when component mounts
    const handleMouseClick = () => setCount((prevCount) => prevCount + 1);
    document.addEventListener('click', handleMouseClick);

    // Clean up the event listener when the component unmounts
    return () => {
      document.removeEventListener('click', handleMouseClick);
    };
  }, []); // Empty dependency array means the effect runs only once (on mount)

  return <div>Count: {count}</div>;
}

In this example, the handleMouseClick event listener is added when the component mounts and removed when the component unmounts. This ensures that there is only one event listener attached to the click event at any given time, preventing memory leaks and ensuring proper cleanup.

Advanced Questions

1. Can you discuss a complex scenario where you would use multiple hooks together?

Sure! Let’s consider a complex scenario where we have a chat application. We want to implement real-time updates, maintain user authentication, and handle scroll pagination for fetching older messages.

To achieve this, we can use multiple hooks together:

  1. useState: We’ll use this hook to manage the state of the chat messages and user input.
  2. useEffect: This hook will be used to handle side effects like fetching initial messages, real-time updates, and cleaning up event listeners.
  3. useRef: We’ll use this hook to keep a reference to the container element so we can manage scrolling and load more messages as the user scrolls.
  4. useContext: We can use this hook to manage user authentication and share user-related information across components.
  5. useSocket: A custom hook to handle the WebSocket connection and receive real-time updates from the server.

2. How can you sync state across multiple components using hooks?

To sync state across multiple components using hooks, you can use the useContext hook in React. useContext allows you to share state between multiple components without having to pass props down through all levels of the component tree.

Here’s a basic example:

JSX
// Create a new context
const StateContext = React.createContext();

// Define a provider to wrap the components that need access to the state
function StateProvider({ children }) {
  const [state, setState] = useState(initialState);

  return (
    <StateContext.Provider value={{ state, setState }}>
      {children}
    </StateContext.Provider>
  );
}

// Use the state in any child component
function ChildComponent() {
  const { state, setState } = useContext(StateContext);

  return (
    <div>
      <p>Value from state: {state.someValue}</p>
      <button onClick={() => setState({ someValue: 'New Value' })}>
        Change State
      </button>
    </div>
  );
}

// Wrap your root component with the StateProvider
function App() {
  return (
    <StateProvider>
      <ChildComponent />
    </StateProvider>
  );
}

In this example, StateProvider wraps the components that need access to the shared state. The useContext hook in ChildComponent allows it to access and update the state managed by StateProvider.

3. Can you describe how you might implement server-side rendering with hooks?

To implement server-side rendering (SSR) with hooks in a React application, we need to ensure that our components can fetch data on both the client and server sides. Here are the general steps to achieve SSR with hooks:

  1. Use useEffect with an empty dependency array ([]) to fetch data in the component. This ensures the effect runs only on the client side.
  2. On the server side, use a library like react-dom/server to render the components to a string and fetch the data separately.
  3. Pass the fetched data as props to the components before rendering on the server.

Here’s an example using the useEffect hook with axios for data fetching:

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

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

  useEffect(() => {
    axios.get('/api/data')
      .then(response => setData(response.data))
      .catch(error => console.error(error));
  }, []); // Empty dependency array to run the effect only once on the client side

  return (
    <div>
      {data.map(item => <p key={item.id}>{item.name}</p>)}
    </div>
  );
}

In this example, the component MyComponent fetches data from the server using useEffect, and the fetched data is then used to render the component. On the server side, you’d use a similar approach to fetch the data and pass it as props to the MyComponent before rendering it with SSR.

4. How can you implement optimistic updates in a React application with hooks?

Optimistic updates are a technique used to provide an immediate UI response to user actions, even before the server confirms the success of the action. In case of a failure, the UI is updated to reflect the actual server state. To implement optimistic updates with hooks, you can use useState to manage the state locally before the server responds.

Here’s an example of how to implement optimistic updates for a simple todo application:

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

function TodoItem({ todo, onDelete }) {
  const [isDeleting, setIsDeleting] = useState(false);

  const handleDelete = () => {
    setIsDeleting(true);
    // Optimistically update the UI immediately
    onDelete(todo.id)
      .catch(() => {
        setIsDeleting(false); // Revert the optimistic update on failure
      });
  };

  return (
    <div>
      <span>{todo.title}</span>
      <button onClick={handleDelete} disabled={isDeleting}>
        {isDeleting ? 'Deleting...' : 'Delete'}
      </button>
    </div>
  );
}

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, title: 'Learn React' },
    { id: 2, title: 'Build a project' },
  ]);

  const handleDeleteTodo = (id) => {
    // Simulate API request
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        const updatedTodos = todos.filter(todo => todo.id !== id);
        setTodos(updatedTodos);
        resolve();
      }, 1000);
    });
  };

  return (
    <div>
      {todos.map(todo => (
        <TodoItem key={todo.id} todo={todo} onDelete={handleDeleteTodo} />
      ))}
    </div>
  );
}

In this example, when the “Delete” button is clicked, the handleDelete function is called, which sets the isDeleting state to true. This immediately updates the UI to show “Deleting…” text while the actual API call is made. If the API call is successful, the setTodos function is called to remove the todo from the state and update the UI. If there’s an error, the optimistic update is reverted by setting isDeleting back to false.

5. Discuss how you would use hooks to animate a component.

To animate a component using hooks, you can utilize the useEffect and useState hooks to manage the animation state and transitions. Additionally, you may use CSS transitions or a library like react-spring to handle the animations. Let’s demonstrate using CSS transitions:

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

function AnimatedComponent() {
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    setIsVisible(true);
  }, []);

  const handleHide = () => {
    setIsVisible(false);
  };

  return (
    <div className={`animated-component ${isVisible ? 'visible' : 'hidden'}`}>
      <h2>Animated Component</h2>
      <button onClick={handleHide}>Hide</button>
    </div>
  );
}

CSS:

JSX
.animated-component {
  width: 200px;
  height: 100px;
  background-color: blue;
  opacity: 0;
  transition: opacity 0.5s;
}

.animated-component.visible {
  opacity: 1;
}

.animated-component.hidden {
  display: none;
}

In this example, the AnimatedComponent has an initial state of isVisible set to false. When the component mounts, the useEffect hook sets the isVisible state to true, causing the component to become visible with the CSS transition. When the “Hide” button is clicked, the handleHide function sets the isVisible state to false, and the component fades out due to the CSS transition.

6. How would you handle focus within a form using React hooks?

To handle focus within a form using React hooks, you can use the useRef hook to create a reference to the input element and then use the focus() method to programmatically set focus to the input when needed.

Here’s an example:

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

function MyForm() {
  const inputRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    // Process form data here...
    console.log(inputRef.current.value);
  };

  const handleFocusInput = () => {
    inputRef.current.focus();
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" ref={inputRef} />
      <button type="submit">Submit</button>
      <button type="button" onClick={handleFocusInput}>Focus Input</button>
    </form>
  );
}

In this example, we create a ref using useRef and assign it to the inputRef constant. When the “Focus Input” button is clicked, the handleFocusInput function is called, which calls focus() on the input element referenced by inputRef, setting focus to the input field.

7. How would you handle global state in a large application with hooks?

To handle global state in a large React application using hooks, you can use the useContext hook along with useReducer or a state management library like Redux or MobX.

Here’s how you can implement global state using the useContext and useReducer hooks:

JSX
import React, { createContext, useContext, useReducer } from 'react';

// Create a context for the global state
const GlobalStateContext = createContext();

// Define the initial state
const initialState = {
  count: 0,
  user: null,
  // ... other global state properties
};

// Define a reducer function to manage state updates
function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'SET_USER':
      return { ...state, user: action.payload };
    // ... other cases for different state updates
    default:
      return state;
  }
}

// Create a provider to wrap the top-level component and pass the global state and dispatch function down
function GlobalStateProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <GlobalStateContext.Provider value={{ state, dispatch }}>
      {children}
    </GlobalStateContext.Provider>
  );
}

// Custom hook to access the global state and dispatch function
function useGlobalState() {
  const context = useContext(GlobalStateContext);
  if (!context) {
    throw new Error('useGlobalState must be used within a GlobalStateProvider');
  }
  return context;
}

// Usage in a component
function MyComponent() {
  const { state, dispatch } = useGlobalState();

  const handleIncrement = () => {
    dispatch({ type: 'INCREMENT' });
  };

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={handleIncrement}>Increment</button>
    </div>
  );
}

// Wrap your root component with the GlobalStateProvider
function App() {
  return (
    <GlobalStateProvider>
      <MyComponent />
    </GlobalStateProvider>
  );
}

8. Can you explain the potential performance issues with the useCallback hook and how to avoid them?

The useCallback hook is used to memoize functions, preventing unnecessary re-creations of the function when the component re-renders. While this can improve performance, there are potential issues to be aware of:

  1. Recreating Callbacks: If you use useCallback with an empty dependency array, the callback will be created only once, which can lead to stale closures. If the callback relies on state or props that change, you should include those dependencies in the dependency array to recreate the callback when needed.
JSX
   const handleClick = useCallback(() => {
     console.log(someState); // This should be added to the dependency array
   }, []);
  1. Excessive Memoization: Using useCallback for all functions in your component might lead to excessive memoization, which can impact performance. Not all functions need to be memoized; only use useCallback for functions that are passed down to child components or used as dependencies in other hooks.
  2. Unintentional Re-renders: If you pass a callback created using useCallback as a prop to a child component, and the parent component re-renders for some other reason, the child component might re-render unnecessarily due to the reference change of the callback.

To avoid potential performance issues with useCallback, consider the following guidelines:

  • Use useCallback only when necessary, such as for functions that need to be memoized to prevent excessive re-renders.
  • Include all relevant dependencies in the dependency array to ensure the callback updates when needed.
  • Avoid using useCallback for every function in your component; not all functions require memoization.
  • Be mindful of passing memoized callbacks as props to child components, as it might lead to unnecessary re-renders in certain cases.

9. How do you debug when a custom hook is causing re-renders?

When a custom hook is causing re-renders, there are several debugging techniques you can use:

  1. Dependency Array Check: Ensure that the dependency array passed to useEffect or useMemo in your custom hook is correct. If it’s missing a dependency or includes unnecessary dependencies, it can lead to unnecessary re-renders.
  2. React DevTools: Use React DevTools to inspect component re-renders and check if your custom hook is responsible for causing frequent re-renders. The component tree in the DevTools will show which components are being re-rendered and how often.
  3. Use console.log: Add console logs inside your custom hook to track when it’s being called and the values of relevant variables. This can help you identify unexpected re-renders and narrow down the issue.
  4. React.StrictMode: Wrap your application with <React.StrictMode> to enable additional checks and warnings during development. This can help catch potential issues with re-renders and other React-related problems.
  5. Performance Profiling: Use the performance tab in Chrome DevTools to analyze the performance of your custom hook and identify bottlenecks that might be causing re-renders.
  6. useDebugValue: If you want to provide additional information about your custom hook to React DevTools, you can use the useDebugValue hook to label custom hooks in DevTools.

10. How can you persist the state of a component using React Hooks between different sessions?

To persist the state of a component using React hooks between different sessions (i.e., across page reloads or when the user revisits the page), you can use the localStorage or sessionStorage APIs. These APIs allow you to store data on the client’s browser and retrieve it later.

Here’s an example of persisting a counter state using localStorage:

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

function CounterComponent() {
  const [count, setCount] = useState(() => {
    // Get the initial count from localStorage, or set it to 0 if not available
    const storedCount = localStorage.getItem('count');
    return storedCount ? parseInt(storedCount, 10) : 0;
  });

  useEffect(() => {
    // Save the current count to localStorage whenever it changes
    localStorage.setItem('count', count.toString());
  }, [count]);

  const handleIncrement = () => {
    setCount((prevCount) => prevCount + 1);
  };

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

In this example, the useState hook’s initial state is set based on the value retrieved from localStorage. The useEffect hook is used to save the updated count to localStorage whenever it changes.

Remember that data stored in localStorage is persistent and will be available even after the user closes and reopens the browser. If you want the data to be available only for the current session, you can use sessionStorage instead.

11. How would you implement a timer or interval with React Hooks?

To implement a timer or interval using React hooks, you can use the useEffect hook to start and stop the interval. Additionally, you can use the useState hook to manage the time state.

Here’s an example of implementing a simple timer:

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

function Timer() {
  const [time, setTime] = useState(0);
  const [isRunning, setIsRunning] = useState(false);

  useEffect(() => {
    let interval;

    if (isRunning) {
      interval = setInterval(() => {
        setTime((prevTime) => prevTime + 1);
      }, 1000);
    }

    return () => clearInterval(interval);
  }, [isRunning]);

  const handleStart = () => {
    setIsRunning(true);
  };

  const handleStop = () => {
    setIsRunning(false);
  };

  const handleReset = () => {
    setTime(0);
  };

  return (
    <div>
      <p>Time: {time} seconds</p>
      {isRunning ? (
        <button onClick={handleStop}>Stop</button>
      ) : (
        <button onClick={handleStart}>Start</button>
      )}
      <button onClick={handleReset}>Reset</button>
    </div>
  );
}

In this example, the useEffect hook starts an interval that increments the time state by 1 every second when isRunning is true. The handleStart, handleStop, and handleReset functions modify the isRunning and time states to control the timer’s behavior.

12. How do you deal with event listeners in useEffect?

When using event listeners with useEffect, it’s essential to add and remove event listeners correctly to avoid memory leaks and potential issues with re-rendering. You should use the useEffect hook’s cleanup function to remove event listeners when the component unmounts.

Here’s an example of how to handle event listeners in useEffect:

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

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

  const handleKeyPress = (event) => {
    if (event.key === 'Enter') {
      setCount((prevCount) => prevCount + 1);
    }
  };

  useEffect(() => {
    document.addEventListener('keydown', handleKeyPress);

    // Cleanup function to remove the event listener when the component unmounts
    return () => {
      document.removeEventListener('keydown', handleKeyPress);
    };
  }, []); // Empty dependency array to run the effect only once

  return (
    <div>
      <p>Press Enter key to increase count: {count}</p>
    </div>
  );
}

13. How does the useReducer hook differ from the useState hook, and when would you choose to use one over the other?

The useReducer and useState hooks in React are both used for managing state within functional components, but they have some key differences.

The useState hook is the simplest way to add state to a functional component. It returns a pair: the current state value and a function to update it. For example:

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

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

  const increment = () => {
    setCount(count + 1);
  };

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

On the other hand, the useReducer hook is more powerful and suitable for managing complex state logic. It follows the same pattern as Redux reducers, where it takes a reducer function and an initial state. The reducer function receives the current state and an action, and based on the action, it returns a new state. For example:

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

const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    default:
      return state;
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  const increment = () => {
    dispatch({ type: 'INCREMENT' });
  };

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

You would choose useState for simple state management needs like a single value or a small number of closely related values. useReducer, on the other hand, is a better choice when you have more complex state that may involve multiple values and actions, or when you need to handle state transitions in a more organized and predictable manner.

14. Explain the useRef hook in React and how it can be used to preserve state between renders.

The useRef hook in React is primarily used to persist values between renders without triggering a re-render of the component. It creates a mutable object referred to as a “ref” that can hold a value and persists it throughout the component’s lifecycle.

Here’s an example of how useRef can be used to preserve state between renders:

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

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

  useEffect(() => {
    // Focus on the input element when the component mounts
    inputRef.current.focus();
  }, []);

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
    </div>
  );
};

In this example, the inputRef is created using useRef(), and we attach it to the input element using the ref attribute. The useEffect hook with an empty dependency array ensures that the input element is focused only once when the component mounts.

15. What is the useImperativeHandle hook, and in what situations would you use it?

The useImperativeHandle hook is a way to customize the instance value that’s exposed to parent components when using ref with a child component. It allows a parent component to gain control over certain methods or properties of a child component’s exposed ref.

You might use useImperativeHandle in situations where you want to expose specific functionality from a child component’s instance to the parent, but you want to hide or restrict access to other internal methods or properties.

Here’s an example of how useImperativeHandle can be used:

JSX
import React, { useRef, useImperativeHandle, forwardRef } from 'react';

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  // Expose a method to focus on the input
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));

  return (
    <input ref={inputRef} type="text" />
  );
});

// Parent component
const ParentComponent = () => {
  const fancyInputRef = useRef(null);

  const handleButtonClick = () => {
    // Call the custom focus method exposed by the child component
    fancyInputRef.current.focus();
  };

  return (
    <div>
      <FancyInput ref={fancyInputRef} />
      <button onClick={handleButtonClick}>Focus Input</button>
    </div>
  );
};

In this example, the FancyInput component exposes a focus method through useImperativeHandle, and the parent component (ParentComponent) can use it to focus on the input element when the button is clicked.

16. How can you use the useContext hook to handle global state management in a React application?

The useContext hook in React is used to consume values from a React context that has been created using React.createContext. It allows components to access global state without prop drilling (passing down props through multiple layers of components).

To handle global state management using useContext, you typically follow these steps:

  1. Create a context using React.createContext() and provide a default value.
  2. Wrap the root component(s) of your application with the context provider (Context.Provider).
  3. Use useContext in child components to access the context’s value.

Here’s an example of how to use useContext for global state management:

JSX
// ThemeContext.js
import React, { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

export const useTheme = () => useContext(ThemeContext);

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

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

In the example above, we create a ThemeContext using createContext and provide it with a default value that includes the theme state and a function setTheme to update it. Then, we create a custom hook useTheme to consume the context value more easily.

Now, we can wrap our application’s root component with the ThemeProvider:

JSX
// App.js
import React from 'react';
import { ThemeProvider } from './ThemeContext';
import MainComponent from './MainComponent';

const App = () => {
  return (
    <ThemeProvider>
      <MainComponent />
    </ThemeProvider>
  );
};

Finally, in any child component of `MainComponent, we can access the global state using theuseTheme` hook:

JSX
// MainComponent.js
import React from 'react';
import { useTheme } from './ThemeContext';

const MainComponent = () => {
  const { theme, setTheme } = useTheme();

  const toggleTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light');
  };

  return (
    <div>
      <h1>Current Theme: {theme}</h1>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
};

Now, any component within the ThemeProvider can access the theme and setTheme values without explicitly passing them down as props.

17. Can you illustrate how to create a custom hook that encapsulates API calls for data fetching?

Creating a custom hook to encapsulate API calls for data fetching can make your code more reusable and maintainable. Let’s create a custom hook called useDataFetching that handles fetching data from an API using axios (a popular HTTP client library).

Assuming you have axios installed as a dependency, here’s how the custom hook can be implemented:

JSX
import { useState, useEffect } from 'react';
import axios from 'axios';

const useDataFetching = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await axios.get(url);
        setData(response.data);
        setLoading(false);
      } catch (error) {
        setError(error);
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

export default useDataFetching;

Now you can use this custom hook in any component to fetch data:

JSX
import React from 'react';
import useDataFetching from './useDataFetching';

const DataComponent = () => {
  const { data, loading, error } = useDataFetching('https://api.example.com/data');

  if (loading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <div>
      {data && data.map(item => <p key={item.id}>{item.name}</p>)}
    </div>
  );
};

export default DataComponent;

This custom hook takes a URL as a parameter, and it handles fetching the data and managing the loading and error states. The component using this hook can focus solely on rendering the data without dealing with the complexities of fetching data from an API.

18. What is the purpose of the useDebugValue hook, and how can it aid in the development process?

The useDebugValue hook in React is used to display custom labels for custom hooks in React DevTools. It helps developers add additional information about custom hooks, making it easier to understand their behavior when inspecting them in the browser’s React DevTools.

Without using useDebugValue, custom hooks might appear with generic labels like “Custom Hook” in React DevTools, which can be confusing and not very informative. By adding useDebugValue, developers can provide more descriptive labels and display relevant information.

Here’s an example of how useDebugValue can be used:

JSX
import { useState, useEffect, useDebugValue } from 'react';

const useDataFetching = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useDebugValue(`Fetching data from: ${url}`);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
        setLoading(false);
      } catch (error) {
        setError(error);
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

export default useDataFetching;

In this example, we use useDebugValue to display a message in React DevTools indicating which URL we are fetching data from.

19. Explain the rules and best practices for using React hooks within custom hooks to ensure proper functionality.

When using React hooks within custom hooks, there are some rules and best practices to follow to ensure proper functionality and avoid any unexpected issues:

  1. Only Call Hooks at the Top Level: React hooks should only be called at the top level of a custom hook, not inside loops, conditions, or nested functions. This ensures that hooks are called in the same order on every render.
  2. Use Hooks Conditionally: If a hook call needs to be conditional, ensure it’s called unconditionally during every render. React relies on the order of hook calls to maintain state and updates.
  3. Use Hooks in Functional Components Only: Hooks should only be used in functional components or other custom hooks. Don’t use them in regular JavaScript functions or class components.
  4. Name Your Custom Hooks Properly: Make sure your custom hooks have names that start with “use” to indicate that they are hooks. This is not required for functionality, but it’s a convention that improves code readability.
  5. Only Return Callables from Hooks: A custom hook should return at least one callable function, usually the state updater function(s) or any other functions that need to be used externally.
  6. Dependency Array for useEffect and useCallback: If your custom hook uses useEffect or useCallback, ensure to pass the correct dependency arrays to avoid unintended side effects and unnecessary re-renders.
  7. Document Your Custom Hooks: Document the purpose, usage, and any special considerations of your custom hooks to help other developers understand their functionality.
  8. Test Your Custom Hooks: Test your custom hooks to ensure they work as expected and handle edge cases correctly. React hooks have specific behaviors and rules that might not be obvious without proper testing.

20. How would you handle form validation in React using the useState and useEffect hooks?

Handling form validation in React can be achieved using the useState and useEffect hooks to manage form state and validate inputs based on certain conditions. Here’s a simple example of how to do it:

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

const FormValidationExample = () => {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    password: '',
  });

  const [errors, setErrors] = useState({});

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData({
      ...formData,
      [name]: value,
    });
  };

  const handleSubmit = (e) => {
    e.preventDefault

();
    // Perform any submit logic here
  };

  useEffect(() => {
    // Custom validation logic
    const newErrors = {};

    if (!formData.name) {
      newErrors.name = 'Name is required';
    }

    if (!formData.email) {
      newErrors.email = 'Email is required';
    } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
      newErrors.email = 'Invalid email format';
    }

    if (!formData.password) {
      newErrors.password = 'Password is required';
    } else if (formData.password.length < 6) {
      newErrors.password = 'Password must be at least 6 characters';
    }

    setErrors(newErrors);
  }, [formData]);

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Name:</label>
        <input
          type="text"
          name="name"
          value={formData.name}
          onChange={handleChange}
        />
        {errors.name && <p>{errors.name}</p>}
      </div>

      <div>
        <label>Email:</label>
        <input
          type="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
        />
        {errors.email && <p>{errors.email}</p>}
      </div>

      <div>
        <label>Password:</label>
        <input
          type="password"
          name="password"
          value={formData.password}
          onChange={handleChange}
        />
        {errors.password && <p>{errors.password}</p>}
      </div>

      <button type="submit">Submit</button>
    </form>
  );
};

export default FormValidationExample;

In this example, the handleChange function updates the form data state whenever any input value changes. The useEffect hook is used to perform custom validation whenever the form data changes, updating the errors state with any validation errors.

21. Describe how you could implement pagination using the useEffect hook to fetch data in response to user actions.

Implementing pagination using the useEffect hook involves managing the current page state and fetching data from the API accordingly. Here’s a step-by-step guide on how to achieve this:

Assuming you have an API that supports pagination and returns data in pages with a query parameter page, let’s create a custom hook called usePagination to handle pagination logic:

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

const usePagination = (initialPage = 1) => {
  const [currentPage, setCurrentPage] = useState(initialPage);
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);

  const fetchData = async (page) => {
    try {
      const response = await fetch(`https://api.example.com/data?page=${page}`);
      const result = await response.json();
      setData(result);
      setLoading(false);
    } catch (error) {
      console.error('Error fetching data:', error);
      setLoading(false);
    }
  };

  useEffect(() => {
    setLoading(true);
    fetchData(currentPage);
  }, [currentPage]);

  const nextPage = () => setCurrentPage((prev) => prev + 1);
  const prevPage = () => setCurrentPage((prev) => Math.max(prev - 1, 1));

  return { data, loading, currentPage, nextPage, prevPage };
};

export default usePagination;

In the example above, the usePagination hook manages the current page, fetches data based on the current page using fetchData, and returns the current data, loading state, current page, and functions to navigate to the next and previous pages.

Now, you can use this custom hook in your component to implement pagination:

JSX
import React from 'react';
import usePagination from './usePagination';

const PaginatedComponent = () => {
  const { data, loading, currentPage, nextPage, prevPage } = usePagination();

  if (loading) {
    return <p>Loading...</p>;
  }

  return (
    <div>
      {data.map((item) => (
        <p key={item.id}>{item.name}</p>
      ))}

      <button onClick={prevPage} disabled={currentPage === 1}>
        Previous Page
      </button>
      <button onClick={nextPage}>Next Page</button>
    </div>
  );
};

export default PaginatedComponent;

In this example, the PaginatedComponent uses the usePagination hook to fetch data based on the current page. It renders the data and provides buttons to navigate to the previous and next pages. The hook takes care of fetching the appropriate data whenever the page changes.

22. What is the useLayoutEffect hook, and how does it differ from useEffect in terms of timing and use cases?

The useLayoutEffect hook in React is very similar to the useEffect hook, but with one key difference: the timing of when it runs during the component lifecycle.

The useEffect hook runs asynchronously after the render phase is complete and the DOM has been updated. It’s commonly used for side effects that don’t block or delay rendering, such as data fetching, subscriptions, or updating the document title.

On the other hand, the useLayoutEffect hook runs synchronously immediately after the render phase but before the browser’s layout and paint phases. This means it executes before the DOM is updated, allowing you to perform actions that require the updated DOM measurements or synchronous operations.

Use useLayoutEffect when you need to perform DOM manipulations that could cause visual updates and you want those updates to be synchronous with the render. In most cases, useEffect is sufficient for handling side effects, and useLayoutEffect is used less frequently.

Here’s an example to illustrate the difference:

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

const LayoutEffectExample = () => {
  const [width, setWidth] = useState(0);

  useEffect(() => {
    // This effect runs asynchronously after the render is complete
    console.log('useEffect: After render');
  });

  useLayoutEffect(() => {
    // This effect runs synchronously before the DOM is updated
    console.log('useLayoutEffect: Before render');

    const handleResize = () => {
      setWidth(window.innerWidth);
    };

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  });

  return <p>Window width: {width}px</p>;
};

export default LayoutEffectExample;

In this example, useLayoutEffect is used to update the width state when the window is resized. The effect runs before the DOM is updated, ensuring that the state update is in sync with the DOM.

MCQ Questions

1. What is a React hook?

a) A functional component in React
b) A class component in React
c) A state management library in React
d) A function that adds additional features to functional components in React

Answer: d) A function that adds additional features to functional components in React

2. Which built-in hook in React allows you to manage state in a functional component?

a) useState
b) useEffect
c) useContext
d) useRef

Answer: a) useState

3. Which hook is used to perform side effects in React?

a) useState
b) useEffect
c) useContext
d) useMemo

Answer: b) useEffect

4. How can you access the previous state in React using hooks?

a) By using the useRef hook
b) By using the useState hook
c) By using the useEffect hook
d) By using the useContext hook

Answer: b) By using the useState hook

5. Which hook is used to share state between multiple components in React?

a) useState
b) useEffect
c) useContext
d) useMemo

Answer: c) useContext

6. What is the purpose of the useCallback hook in React?

a) To memoize expensive computations
b) To handle asynchronous operations
c) To manage side effects
d) To create reusable callback functions

Answer: d) To create reusable callback functions

7. Which hook is used to optimize performance by memoizing the result of a function?

a) useState
b) useEffect
c) useContext
d) useMemo

Answer: d) useMemo

8. What is the purpose of the useRef hook in React?

a) To manage state in a functional component
b) To perform side effects in a functional component
c) To store mutable values between renders
d) To share state between multiple components

Answer: c) To store mutable values between renders

9. How can you force a re-render of a component in React using hooks?

a) By using the useState hook
b) By using the useEffect hook
c) By using the useContext hook
d) By updating the component’s props

Answer: a) By using the useState hook

10. What is the purpose of the useReducer hook in React?

a) To manage state in a functional component
b) To perform side effects in a functional component
c) To share state between multiple components
d) To manage complex state transitions in a more controlled way

Answer: d) To manage complex state transitions in a more controlled way

11. How can you prevent a component from re-rendering in React using hooks?

a) By using the useState hook
b) By using the useEffect hook
c) By using the useContext hook
d) By using the useMemo hook

Answer: d) By using the useMemo hook

12. What is the purpose of the useContext hook in React?

a) To manage state in a functional component
b) To perform side effects in a functional component
c) To share state between multiple components
d) To create reusable callback functions

Answer: c) To share state between multiple components

13. Which hook is used to schedule a callback to run after the component has been rendered?

a) useState
b) useEffect
c) useContext
d) useMemo

Answer: b) useEffect

14. What is the purpose of the useLayoutEffect hook in React?

a) To manage state in a functional component before rendering
b) To perform side effects in a functional component after rendering
c) To share state between multiple components
d) To create reusable callback functions

Answer: a) To manage state in a functional component before rendering

15. What is the purpose of the useImperativeHandle hook in React?

a) To manage state in a functional component
b) To perform side effects in a functional component
c) To share state between multiple components
d) To customize the instance value that is exposed to parent components

Answer: d) To customize the instance value that is exposed to parent components

16. Which hook is used to reference a DOM element in React?

a) useState
b) useEffect
c) useContext
d) useRef

Answer: d) useRef

17. What is the purpose of the useDebugValue hook in React?

a) To manage state in a functional component
b) To perform side effects in a functional component
c) To share state between multiple components
d) To provide a custom label for custom hooks in the React DevTools

Answer: d) To provide a custom label for custom hooks in the React DevTools

18. How can you simulate the lifecycle methods componentDidMount and componentDidUpdate using hooks?

a) By using the useState hook
b) By using the useEffect hook
c) By using the useContext hook
d) By using the useMemo hook

Answer: b) By using the useEffect hook

19. What is the purpose of the useErrorBoundary hook in React?

a) To manage errors in a functional component
b) To perform side effects in a functional component
c) To share state between multiple components
d) To handle errors thrown by components in the component tree

Answer: a) To manage errors in a functional component

20. Which hook is used to get the current value of a context in React?

a) useState
b) useEffect
c) useContext
d) useRef

Answer: c) useContext

21. What is the purpose of the useReducer hook in React?

a) To manage state in a functional component
b) To perform side effects in a functional component
c) To share state between multiple components
d) To manage complex state transitions in a more controlled way

Answer: d) To manage complex state transitions in a more controlled way

22. How can you prevent a component from re-rendering in React using hooks?

a) By using the useState hook
b) By using the useEffect hook
c) By using the useContext hook
d) By using the useMemo hook

Answer: d) By using the useMemo hook

23. What is the purpose of the useContext hook in React?

a) To manage state in a functional component
b) To perform side effects in a functional component
c) To share state between multiple components
d) To create reusable callback functions

Answer: c) To share state between multiple components

24. Which hook is used to schedule a callback to run after the component has been rendered?

a) useState
b) useEffect
c) useContext
d) useMemo

Answer: b) useEffect

25. What is the purpose of the useLayoutEffect hook in React?

a) To manage state in a functional component before rendering
b) To perform side effects in a functional component after rendering
c) To share state between multiple components
d) To create reusable callback functions

Answer: a) To manage state in a functional component before rendering

26. What is the purpose of the useImperativeHandle hook in React?

a) To manage state in a functional component
b) To perform side effects in a functional component
c) To share state between multiple components
d) To customize the instance value that is exposed to parent components

Answer: d) To customize the instance value that is exposed to parent components

27. Which hook is used to reference a DOM element in React?

a) useState
b) useEffect
c) useContext
d) useRef

Answer: d) useRef

28. What is the purpose of the useDebugValue hook in React?

a) To manage state in a functional component
b) To perform side effects in a functional component
c) To share state between multiple components
d) To provide a custom label for custom hooks in the React DevTools

Answer: d) To provide a custom label for custom hooks in the React DevTools

29. How can you simulate the lifecycle methods componentDidMount and componentDidUpdate using hooks?

a) By using the useState hook
b) By using the useEffect hook
c) By using the useContext hook
d) By using the useMemo hook

Answer: b) By using the useEffect hook

30. What is the purpose of the useErrorBoundary hook in React?

a) To manage errors in a functional component
b) To perform side effects in a functional component
c) To share state between multiple components
d) To handle errors thrown by components in the component tree

Answer: a) To manage errors in a functional component

Deepak Vishwakarma

Founder

RELATED Articles

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.