Create a Reusable useCounter Hook in React with TypeScript
This challenge asks you to create a custom React hook called useCounter. This hook will encapsulate the logic for managing a simple numerical counter, making it easy to reuse across different components in your application. A well-implemented useCounter hook will streamline state management for common counting scenarios.
Problem Description
You need to develop a custom React hook in TypeScript named useCounter. This hook should manage a numerical state representing a counter. It should expose functions to increment, decrement, and reset the counter. The initial value of the counter should be configurable.
Key Requirements:
- State Management: The hook must internally manage a numerical state (the counter value).
- Initial Value: The hook should accept an optional
initialValueargument, defaulting to0. - Functions: The hook must return the following functions:
increment(): Increases the counter value by 1.decrement(): Decreases the counter value by 1.reset(): Resets the counter value to itsinitialValue.
- Return Value: The hook should return an object containing the current
countand theincrement,decrement, andresetfunctions. - TypeScript: The hook and its return types must be defined using TypeScript for type safety.
Expected Behavior:
When the increment function is called, the counter should increase. When decrement is called, it should decrease. Calling reset should bring the counter back to its initial state.
Edge Cases to Consider:
- What happens if the
initialValueis negative? The hook should handle this gracefully. - Consider how the hook behaves when multiple components use the same instance of the
useCounterhook (though in typical React usage, each component instance would get its own state).
Examples
Example 1:
import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './useCounter'; // Assuming your hook is in this file
// Scenario: Basic usage
const { result } = renderHook(() => useCounter());
// Initial state
console.log(result.current.count); // Output: 0
// Increment
act(() => {
result.current.increment();
});
console.log(result.current.count); // Output: 1
// Decrement
act(() => {
result.current.decrement();
});
console.log(result.current.count); // Output: 0
// Reset
act(() => {
result.current.increment();
result.current.increment();
result.current.reset();
});
console.log(result.current.count); // Output: 0
Explanation: This example demonstrates the basic functionality. The counter starts at 0, increments to 1, decrements back to 0. Then, it's incremented twice and reset to its initial value of 0.
Example 2:
import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './useCounter';
// Scenario: Initial value provided
const { result } = renderHook(() => useCounter(5));
// Initial state
console.log(result.current.count); // Output: 5
// Increment
act(() => {
result.current.increment();
});
console.log(result.current.count); // Output: 6
// Reset
act(() => {
result.current.reset();
});
console.log(result.current.count); // Output: 5
Explanation:
Here, the useCounter hook is initialized with a value of 5. The increment and reset functions operate as expected relative to this initial value.
Example 3:
import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './useCounter';
// Scenario: Negative initial value
const { result } = renderHook(() => useCounter(-3));
// Initial state
console.log(result.current.count); // Output: -3
// Decrement
act(() => {
result.current.decrement();
});
console.log(result.current.count); // Output: -4
// Reset
act(() => {
result.current.reset();
});
console.log(result.current.count); // Output: -3
Explanation: This example shows that the hook correctly handles negative initial values and that the increment/decrement/reset operations work consistently with negative numbers.
Constraints
- The
useCounterhook must be a functional React hook. - The
initialValuemust be anumber. - The
countreturned by the hook must be anumber. - The
increment,decrement, andresetfunctions should not accept any arguments. - The hook should be written in TypeScript.
Notes
- You will likely need to use the
useStatehook from React internally. - Remember to define clear TypeScript types for the hook's arguments and its return value.
- Consider how you will export the hook for use in other components.
- When testing hook behavior, especially state updates, use
actfrom@testing-library/react-hooksor@testing-library/reactto ensure state updates are batched correctly.