Write useCounter(initial, options) and export it from useCounter.js. It keeps a number, like how many cups of a tea are in a cart, and returns an object: { count, increment, decrement, reset }.
initial is where the count starts, and it's 0 when you leave it out. options is an optional object with min, max, and step. Leave out min or max and there's no limit on that side. step is 1 unless you give one.
increment() adds step and decrement() takes it away, but the count never goes above max or below min: a step that would pass a limit stops at it. With max: 10 and step: 4, going up from 8 gives 10, and going up again stays at 10.
If initial is outside the limits, the count starts at the nearest limit. reset() puts the count back where it started.
Calling increment() twice in the same event adds two steps.
function Cups() {
const { count, increment, decrement, reset } = useCounter(1, { min: 1, max: 5 });
return (
<div>
<p>Cups: {count}</p>
<button onClick={decrement}>Fewer</button>
<button onClick={increment}>More</button>
<button onClick={reset}>Reset</button>
</div>
);
}
The Preview shows Demo.jsx, a cup counter that uses your hook.
const { result } = renderHook(() => useCounter());
expect(result.current.count).toBe(0);
const { result } = renderHook(() => useCounter(3));
expect(result.current.count).toBe(3);
const { result } = renderHook(() => useCounter());
act(() => result.current.decrement());
expect(result.current.count).toBe(-1);
act(() => result.current.increment());
act(() => result.current.increment());
expect(result.current.count).toBe(1);
const { result } = renderHook(() => useCounter(5, { step: 2 }));
act(() => result.current.increment());
expect(result.current.count).toBe(7);
act(() => result.current.decrement());
act(() => result.current.decrement());
expect(result.current.count).toBe(3);
const { result } = renderHook(() => useCounter(8, { max: 10, step: 4 }));
act(() => result.current.increment());
expect(result.current.count).toBe(10);
act(() => result.current.increment());
expect(result.current.count).toBe(10);
const { result } = renderHook(() => useCounter(3, { min: 0, step: 2 }));
act(() => result.current.decrement());
expect(result.current.count).toBe(1);
act(() => result.current.decrement());
expect(result.current.count).toBe(0);
act(() => result.current.decrement());
expect(result.current.count).toBe(0);
const { result } = renderHook(() => useCounter(2));
act(() => result.current.increment());
act(() => result.current.increment());
expect(result.current.count).toBe(4);
act(() => result.current.reset());
expect(result.current.count).toBe(2);
const high = renderHook(() => useCounter(50, { min: 1, max: 10 }));
expect(high.result.current.count).toBe(10);
act(() => high.result.current.decrement());
act(() => high.result.current.reset());
expect(high.result.current.count).toBe(10);
const low = renderHook(() => useCounter(-4, { min: 1, max: 10 }));
expect(low.result.current.count).toBe(1);
const { result } = renderHook(() => useCounter());
const { increment } = result.current;
act(() => {
increment();
increment();
});
expect(result.current.count).toBe(2);
function Cups() {
const { count, increment, decrement } = useCounter(1, { min: 1, max: 3 });
return (
<div>
<p>Cups: {count}</p>
<button onClick={decrement}>Fewer</button>
<button onClick={increment}>More</button>
</div>
);
}
render(<Cups />);
const more = screen.getByRole("button", { name: "More" });
await user.click(more);
await user.click(more);
await user.click(more);
expect(screen.getByText("Cups: 3")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "Fewer" }));
expect(screen.getByText("Cups: 2")).toBeInTheDocument();
Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.
Ctrl + Enter runs the tests (⌘ + Enter on a Mac).
I'm here to help with useCounter. I give hints, never the solution, so you get to solve it yourself. Ask for a hint, select code to ask about it, or press Ask Tuto why on a failing test.
Your first 3 answers need no account. for free to keep asking.