Implement an optimized version of the useCounter
hook. The returned methods should be memoized, the same function instance is returned across re-renders.
export default function Component() {const { count, increment, decrement, reset, setCount } = useCounter();return (<div><p>Counter: {count}</p><button onClick={increment}>Increment</button><button onClick={decrement}>Decrement</button><button onClick={reset}>Reset</button></div>);}
initialValue: number
: Initial value of the counter state. If not provided, it should default to 0
.The useCounter
hook returns an object
with the following properties:
count: number
: The current counter valueincrement: () => void
: A function to increment the counter valuedecrement: () => void
: A function to decrement the counter valuereset: () => void
: A function to reset the counter value to initialValue
, or 0
if not providedsetCount: (value: number) => void
: A function to set the counter value to value
, it has the same signature as setState
increment
, decrement
, reset
, and setCount
must be the same function instance across re-renders.
Implement an optimized version of the useCounter
hook. The returned methods should be memoized, the same function instance is returned across re-renders.
export default function Component() {const { count, increment, decrement, reset, setCount } = useCounter();return (<div><p>Counter: {count}</p><button onClick={increment}>Increment</button><button onClick={decrement}>Decrement</button><button onClick={reset}>Reset</button></div>);}
initialValue: number
: Initial value of the counter state. If not provided, it should default to 0
.The useCounter
hook returns an object
with the following properties:
count: number
: The current counter valueincrement: () => void
: A function to increment the counter valuedecrement: () => void
: A function to decrement the counter valuereset: () => void
: A function to reset the counter value to initialValue
, or 0
if not providedsetCount: (value: number) => void
: A function to set the counter value to value
, it has the same signature as setState
increment
, decrement
, reset
, and setCount
must be the same function instance across re-renders.
The useCounter
hook uses useState
to manage the number
state. The setter functions can be implemented in terms of setValue
from the useState
hook.
import { useState } from 'react';export default function useCounter(initialValue = 0) {const [count, setCount] = useState(initialValue);return {count,increment: () => setCount((x) => x + 1),decrement: () => setCount((x) => x - 1),reset: () => setCount(initialValue),setCount,};}
However, writing the hook this way means that components that rely on the utility functions will always be re-rendered since these functions are always freshly created on each render. To avoid this, we can use useCallback
to memoize them.
import { Dispatch, SetStateAction, useCallback, useState } from 'react';interface UseCounterReturn {count: number;increment: () => void;decrement: () => void;reset: () => void;setCount: Dispatch<SetStateAction<number>>;}export default function useCounter(initialValue = 0): UseCounterReturn {const [count, setCount] = useState(initialValue);const increment = useCallback(() => {setCount((x) => x + 1);}, []);const decrement = useCallback(() => {setCount((x) => x - 1);}, []);const reset = useCallback(() => {setCount(initialValue);}, [initialValue]);return {count,increment,decrement,reset,setCount,};}
To ensure consistency, the increment
and decrement
functions use an updater function to calculate the new value based on the previous value. As an added bonus, if you're wrapping them in useCallback
, you don't have to add initialValue
to the dependency array and increment
and decrement
will always be memoized once.
If you're using TypeScript, the tricky part is figuring out the right type for setCount
since it can also accept an updater function. Simply hover the setter function from useState
in your favourite IDE and you'll see the type signature in the form of Dispatch<SetStateAction<...>>
where the type of the state is ...
. Dispatch
and SetStateAction
can be imported from react
.
If you prefer to be verbose, it essentially boils down to this.
type SetCount = (valueOrUpdater: number | ((previousValue: number) => number),) => void;
console.log()
statements will appear here.