Hone logo
Hone
Problems

Implementing a useNumber Hook in React

The useNumber hook is a utility that simplifies managing numerical values in React components, providing features like clamping, step-based increment/decrement, and formatting. This challenge asks you to implement this hook, enabling developers to easily handle numerical inputs and display them in a controlled and user-friendly manner. It's a common pattern for forms and interactive elements requiring numerical input.

Problem Description

You are tasked with creating a custom React hook called useNumber. This hook should accept an initial value, a minimum value, a maximum value, and a step value as arguments. It should return an object containing the following:

  • number: The current numerical value, clamped between the minimum and maximum values.
  • increment: A function that increments the number by the step value, ensuring the value remains within the specified bounds.
  • decrement: A function that decrements the number by the step value, ensuring the value remains within the specified bounds.
  • setNumber: A function that allows direct setting of the number value, also clamping it to the bounds.
  • reset: A function that resets the number to its initial value.

The hook should handle edge cases gracefully, such as invalid input types or zero/negative step values. If the step is zero, increment and decrement should be no-ops.

Key Requirements:

  • The hook must be written in TypeScript.
  • The returned number value should always be a number.
  • The increment and decrement functions should update the number state correctly.
  • The setNumber function should update the number state correctly and clamp the value.
  • The reset function should return the number to its initial value.
  • The hook should handle invalid input types (e.g., non-numeric initial value) by defaulting to a reasonable value (0).
  • The hook should handle zero or negative step values gracefully (increment/decrement should not change the value).

Expected Behavior:

  • When the component using the hook initially renders, the number should be set to the initial value (or 0 if the initial value is invalid).
  • Calling increment should increase the number by the step value, but not exceed the max value.
  • Calling decrement should decrease the number by the step value, but not fall below the min value.
  • Calling setNumber with a value outside the bounds should clamp the value to the nearest bound.
  • Calling reset should set the number back to the initial value.

Examples

Example 1:

Input: useNumber(10, 0, 100, 5)
Output: { number: 10, increment: ƒ, decrement: ƒ, setNumber: ƒ, reset: ƒ }
Explanation: The hook is initialized with an initial value of 10, a minimum of 0, a maximum of 100, and a step of 5. The initial `number` is 10.

Example 2:

Input: useNumber(50, 0, 100, 10); increment() called multiple times.
Output: number changes from 50 to 60 to 70 to 80 to 90 to 100.  Further increments have no effect.
Explanation: The `increment` function increases the number by 10 until it reaches the maximum value of 100.

Example 3:

Input: useNumber(100, 0, 100, -5); decrement() called multiple times.
Output: number changes from 100 to 95 to 90 to 85 to 80 to 75. Further decrements have no effect.
Explanation: The `decrement` function decreases the number by 5 until it reaches the minimum value of 0.

Example 4:

Input: useNumber("abc", 0, 100, 1); setNumber(150);
Output: { number: 100, increment: ƒ, decrement: ƒ, setNumber: ƒ, reset: ƒ }
Explanation: The initial value is invalid ("abc"). The hook defaults to 0. `setNumber(150)` clamps the value to 100 (the maximum).

Constraints

  • The initial value, minimum value, and maximum value must be numbers.
  • The step value must be a number.
  • The component using the hook must be a functional component.
  • The hook should be performant and avoid unnecessary re-renders.
  • The step value can be negative, positive, or zero.

Notes

  • Consider using the useState hook to manage the numerical value.
  • Think about how to handle invalid input types gracefully.
  • Pay close attention to the clamping logic to ensure the value stays within the specified bounds.
  • The reset function should simply set the number back to the initial value, regardless of its current state.
  • Ensure your code is well-documented and easy to understand.
Loading editor...
typescript