Hone logo
Hone
Problems

React useLifecycles Hook Challenge

You're tasked with creating a custom React hook, useLifecycles, that encapsulates the logic for handling component lifecycle methods. This hook should provide a clean and declarative way to manage side effects that occur during the mounting and unmounting phases of a React component.

Problem Description

Your goal is to implement a TypeScript hook named useLifecycles. This hook should accept two optional callback functions: onMount and onUnmount.

  • onMount: This function will be executed once when the component that uses this hook mounts.
  • onUnmount: This function will be executed once when the component that uses this hook unmounts.

The hook should abstract away the complexities of useEffect for these specific lifecycle events, making the code more readable and maintainable.

Key Requirements:

  1. onMount Execution: The onMount callback should be invoked precisely once, after the initial render.
  2. onUnmount Execution: The onUnmount callback should be invoked precisely once, before the component unmounts.
  3. Type Safety: The hook must be written in TypeScript, ensuring type safety for the callback functions.
  4. Dependency Management: The hook should correctly handle dependencies to ensure onMount runs only on mount and onUnmount runs only on unmount.

Expected Behavior:

When a component renders and uses useLifecycles, if an onMount callback is provided, it should execute. When that component is subsequently removed from the DOM, if an onUnmount callback is provided, it should execute.

Edge Cases to Consider:

  • No callbacks provided: The hook should gracefully handle cases where neither onMount nor onUnmount are passed.
  • Only onMount provided: The onUnmount callback should not execute.
  • Only onUnmount provided: The onMount callback should not execute.
  • Callbacks with dependencies: While the primary goal is mount/unmount, consider how the hook would behave if the provided callbacks themselves had internal dependencies. (For this challenge, we'll assume the callbacks don't need external dependencies passed to the useLifecycles hook itself, but rather that the useEffect inside the hook handles the core dependency logic).

Examples

Example 1: Basic Mount and Unmount

import React, { useState } from 'react';
import { useLifecycles } from './useLifecycles'; // Assuming your hook is in this file

function MyComponent() {
  const [message, setMessage] = useState('Initializing...');

  useLifecycles({
    onMount: () => {
      setMessage('Component Mounted!');
      console.log('Component did mount');
    },
    onUnmount: () => {
      console.log('Component will unmount');
    },
  });

  return <div>{message}</div>;
}

// In your App.tsx:
function App() {
  const [showComponent, setShowComponent] = useState(true);

  return (
    <div>
      {showComponent && <MyComponent />}
      <button onClick={() => setShowComponent(false)}>Unmount MyComponent</button>
    </div>
  );
}

Expected Output in Console:

When MyComponent first renders: Component did mount

When the "Unmount MyComponent" button is clicked: Component will unmount

Explanation:

The onMount callback logs a message and updates the component's state upon initial rendering. The onUnmount callback logs a message just before the component is removed from the DOM.

Example 2: Handling Missing Callbacks

import React from 'react';
import { useLifecycles } from './useLifecycles'; // Assuming your hook is in this file

function AnotherComponent() {
  // Only onMount provided
  useLifecycles({
    onMount: () => {
      console.log('AnotherComponent mounted with only onMount.');
    },
  });

  return <div>Checking lifecycles...</div>;
}

// In your App.tsx:
function App() {
  return <AnotherComponent />;
}

Expected Output in Console:

When AnotherComponent first renders: AnotherComponent mounted with only onMount.

(No "unmount" message will appear as no onUnmount callback was provided.)

Example 3: Only Unmount Callback

import React from 'react';
import { useLifecycles } from './useLifecycles'; // Assuming your hook is in this file

function ThirdComponent() {
  // Only onUnmount provided
  useLifecycles({
    onUnmount: () => {
      console.log('ThirdComponent unmounting.');
    },
  });

  return <div>Third component.</div>;
}

// In your App.tsx:
function App() {
  const [showThird, setShowThird] = React.useState(true);

  return (
    <div>
      {showThird && <ThirdComponent />}
      <button onClick={() => setShowThird(false)}>Hide Third</button>
    </div>
  );
}

Expected Output in Console:

When the "Hide Third" button is clicked: ThirdComponent unmounting.

(No "mount" message will appear as no onMount callback was provided.)

Constraints

  • The useLifecycles hook must be implemented in TypeScript.
  • The onMount and onUnmount callbacks should be optional.
  • The hook should leverage React's useEffect hook internally for managing the lifecycle effects.
  • The hook should not introduce any performance bottlenecks. It should be as efficient as a direct useEffect implementation for mount/unmount.

Notes

  • Think about how useEffect handles cleanup functions and empty dependency arrays.
  • Consider the types for the callback functions. They should be functions that return void or a cleanup function.
  • The goal is to create a reusable and declarative pattern for common lifecycle scenarios.
Loading editor...
typescript