Hone logo
Hone
Problems

Implement useUpdateLogger Hook in React

This challenge asks you to create a custom React hook called useUpdateLogger. This hook will be responsible for logging the previous and current values of any state or prop that changes within a component. This is incredibly useful for debugging, understanding component lifecycle, and tracking how data flows through your application.

Problem Description

You need to implement a React hook named useUpdateLogger that takes a single argument: a value. This hook should log to the console the previous value of that argument and its current value whenever the argument changes.

Key Requirements:

  1. Hook Signature: The hook must be named useUpdateLogger and accept one argument of any type.
  2. Logging on Change: When the argument passed to useUpdateLogger changes, the hook should log to the console:
    • The value the argument had before the update.
    • The value the argument has after the update.
  3. Initial Render: On the initial render of the component, the hook should not log anything, as there is no "previous" value to compare.
  4. Type Safety: The hook should be implemented using TypeScript to ensure type safety.

Expected Behavior:

When a component using useUpdateLogger re-renders and the tracked value has changed, the console should display output similar to:

Previous Value: [the old value]
Current Value: [the new value]

Edge Cases:

  • Initial Render: As mentioned, no logging should occur.
  • No Change: If the component re-renders but the tracked value remains the same, no logging should occur.
  • Different Data Types: The hook should gracefully handle tracking values of various data types (primitives, objects, arrays, functions, etc.).

Examples

Example 1: Tracking a String State

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

function MyComponent() {
  const [message, setMessage] = useState<string>('Hello');

  // Track the 'message' state
  useUpdateLogger(message);

  return (
    <div>
      <h1>Message: {message}</h1>
      <button onClick={() => setMessage('World')}>Change Message</button>
      <button onClick={() => setMessage('Hello')}>Keep Message</button>
    </div>
  );
}

Input:

  1. Initial render: message is 'Hello'.
  2. User clicks "Change Message": message becomes 'World'.
  3. User clicks "Keep Message": message becomes 'Hello' again.

Output (in the browser console):

// After clicking "Change Message"
Previous Value: Hello
Current Value: World

// After clicking "Keep Message" (no output because value didn't change)

Example 2: Tracking a Number State

import React, { useState } from 'react';
import { useUpdateLogger } from './useUpdateLogger';

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

  // Track the 'count' state
  useUpdateLogger(count);

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

Input:

  1. Initial render: count is 0.
  2. User clicks "Increment": count becomes 1.
  3. User clicks "Increment" again: count becomes 2.
  4. User clicks "Decrement": count becomes 1.

Output (in the browser console):

// After first "Increment" click
Previous Value: 0
Current Value: 1

// After second "Increment" click
Previous Value: 1
Current Value: 2

// After "Decrement" click
Previous Value: 2
Current Value: 1

Example 3: Tracking an Object

import React, { useState } from 'react';
import { useUpdateLogger } from './useUpdateLogger';

interface User {
  name: string;
  age: number;
}

function UserProfile() {
  const [user, setUser] = useState<User>({ name: 'Alice', age: 30 });

  // Track the 'user' object
  useUpdateLogger(user);

  const updateUserAge = () => {
    setUser({ ...user, age: user.age + 1 });
  };

  return (
    <div>
      <h2>{user.name}</h2>
      <p>Age: {user.age}</p>
      <button onClick={updateUserAge}>Birthday</button>
    </div>
  );
}

Input:

  1. Initial render: user is { name: 'Alice', age: 30 }.
  2. User clicks "Birthday": user becomes { name: 'Alice', age: 31 }.

Output (in the browser console):

// After clicking "Birthday"
Previous Value: { name: 'Alice', age: 30 }
Current Value: { name: 'Alice', age: 31 }

Constraints

  • The hook must be implemented in TypeScript.
  • The hook should only use React's built-in hooks (useState, useEffect, useRef).
  • The solution should be performant and not introduce unnecessary re-renders or computations.
  • The hook should accept any type of value.

Notes

  • Consider how you will store the "previous" value of the argument between renders.
  • useEffect is likely to be your primary tool for this task.
  • Think about the dependencies of your effect.
  • The challenge is to log the values when they change. How can you detect a change?
  • Remember that objects and arrays are compared by reference in JavaScript. Be mindful of this if you're dealing with complex data structures and ensuring your tracking works as expected.
Loading editor...
typescript