Hone logo
Hone
Problems

React Custom Hook: useNumber

Build a custom React hook, useNumber, that manages and manipulates numerical state. This hook should provide a convenient way to handle number values, including incrementing, decrementing, and resetting them, along with the current value.

Problem Description

Your task is to implement a custom React hook named useNumber in TypeScript. This hook should encapsulate the logic for managing a numerical state within a React component. It should provide:

  1. Current Value: The current numerical value being managed.
  2. Increment Function: A function to increase the current value by a specified amount (defaulting to 1).
  3. Decrement Function: A function to decrease the current value by a specified amount (defaulting to 1).
  4. Reset Function: A function to reset the current value back to its initial value.

This hook will be useful for scenarios where you need to manage counters, quantities, scores, or any other numeric input that requires interactive manipulation.

Key Requirements:

  • The hook should accept an initialValue as an argument, which will be the starting number.
  • The hook should return an object containing:
    • value: The current number.
    • increment: A function that takes an optional amount (number) and increases value by that amount. Defaults to incrementing by 1.
    • decrement: A function that takes an optional amount (number) and decreases value by that amount. Defaults to decrementing by 1.
    • reset: A function that resets value to the original initialValue.
  • The hook must be written in TypeScript and strongly typed.

Examples

Example 1: Basic Counter

import { renderHook, act } from '@testing-library/react';
import { useNumber } from './useNumber'; // Assuming you save the hook in this file

function CounterComponent() {
  const { value, increment, decrement, reset } = useNumber(0);

  return (
    <div>
      <p>Count: {value}</p>
      <button onClick={() => increment()}>Increment</button>
      <button onClick={() => decrement()}>Decrement</button>
      <button onClick={() => reset()}>Reset</button>
    </div>
  );
}

// In your test file:
describe('useNumber', () => {
  it('should manage a basic counter', () => {
    const { result } = renderHook(() => useNumber(0));

    expect(result.current.value).toBe(0);

    act(() => {
      result.current.increment();
    });
    expect(result.current.value).toBe(1);

    act(() => {
      result.current.increment(5);
    });
    expect(result.current.value).toBe(6);

    act(() => {
      result.current.decrement();
    });
    expect(result.current.value).toBe(5);

    act(() => {
      result.current.decrement(2);
    });
    expect(result.current.value).toBe(3);

    act(() => {
      result.current.reset();
    });
    expect(result.current.value).toBe(0);
  });
});

Explanation: The useNumber hook is initialized with 0. Incrementing by default increases it by 1. Incrementing with a specific amount adds that amount. Decrementing works similarly. Resetting returns it to the initial 0.

Example 2: Starting with a different number

// In your test file:
describe('useNumber', () => {
  it('should initialize with a different number', () => {
    const { result } = renderHook(() => useNumber(10));

    expect(result.current.value).toBe(10);

    act(() => {
      result.current.increment();
    });
    expect(result.current.value).toBe(11);

    act(() => {
      result.current.reset();
    });
    expect(result.current.value).toBe(10);
  });
});

Explanation: The hook is initialized with 10. Incrementing increases it, and resetting brings it back to 10.

Example 3: Using larger increments/decrements

// In your test file:
describe('useNumber', () => {
  it('should handle larger increments and decrements', () => {
    const { result } = renderHook(() => useNumber(100));

    expect(result.current.value).toBe(100);

    act(() => {
      result.current.decrement(50);
    });
    expect(result.current.value).toBe(50);

    act(() => {
      result.current.increment(75);
    });
    expect(result.current.value).toBe(125);

    act(() => {
      result.current.reset();
    });
    expect(result.current.value).toBe(100);
  });
});

Explanation: Demonstrates using specific, larger amounts for incrementing and decrementing, showing the flexibility of the hook.

Constraints

  • The initialValue and any amount passed to increment or decrement must be numbers.
  • The hook should be implemented using React's useState hook internally.
  • The functions returned by the hook (increment, decrement, reset) should be stable (i.e., not change on every render) if possible, to avoid unnecessary re-renders in consuming components.

Notes

  • Consider how to handle the amount parameter for increment and decrement. What should happen if it's not provided?
  • Ensure your TypeScript types are accurate and expressive.
  • Think about the stability of the returned functions. Using useCallback might be beneficial.
Loading editor...
typescript