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 thenumberby thestepvalue, ensuring the value remains within the specified bounds.decrement: A function that decrements thenumberby thestepvalue, ensuring the value remains within the specified bounds.setNumber: A function that allows direct setting of thenumbervalue, also clamping it to the bounds.reset: A function that resets thenumberto 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
numbervalue should always be a number. - The
incrementanddecrementfunctions should update thenumberstate correctly. - The
setNumberfunction should update thenumberstate correctly and clamp the value. - The
resetfunction should return thenumberto 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
numbershould be set to the initial value (or 0 if the initial value is invalid). - Calling
incrementshould increase thenumberby thestepvalue, but not exceed themaxvalue. - Calling
decrementshould decrease thenumberby thestepvalue, but not fall below theminvalue. - Calling
setNumberwith a value outside the bounds should clamp the value to the nearest bound. - Calling
resetshould set thenumberback 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
useStatehook 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
resetfunction 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.