Implementing Custom Hooks 💥.. usePrevious() & useTimeOut()

The main reason to write a custom hook is for code reusability. For example, instead of writing the same code across multiple components that use the same common stateful logic (say a “setState” or localStorage logic), you can put that code inside a custom hook and reuse it, I will show you how to implement some useful custom hooks.

Lama Ibrahim
3 min readMar 21, 2023

usePrevious

One question that comes up a lot is “When using hooks how do I get the previous value of props or state?”. With React class components you have the componentDidUpdate method which receives previous props and state as arguments or you can update an instance variable (this.previous = value) and reference it later to get the previous value. So how can we do this inside a functional component that doesn’t have lifecycle methods or an instance to store values on? Hooks to the rescue! We can create a custom hook that uses the useRef hook internally for storing the previous value. See the recipe below with inline comments.


import { useRef, useEffect } from "react";

export function usePrevious<T>(value: T): T | undefined {
/* The ref object is a generic container whose current property is mutable
and can hold any value, similar to an instance property on a class */
const ref = useRef();

// Store current value in ref
useEffect(() => {
ref.current = value;
}, [value]); // Only re-run if value changes

// Return previous value (happens before update in useEffect above)
return ref.current;
}

// Ref: https://usehooks.com/usePrevious/

Usage

import React, { useState } from "react";

const prices = [100, 200, 300, 400, 500, 600, 700];

const Price = ({ price }) => {
const prevPrice = usePrevious(price);
const icon = prevPrice && prevPrice < price ? '😡' : '😊';

return (
<div>
Current price: {price}; <br />
Previous price: {prevPrice} {icon}
</div>
);
};

const Page = () => {
const [price, setPrice] = useState(100);

const onPriceChange = (e) => setPrice(Number(e.target.value));

return (
<>
<select value={price} onChange={onPriceChange}>
{prices.map((price) => (<option value={price}>{price}$</option>))}
</select>
<Price price={price} />
</>
);
}

useTimeOut

A hook to easily use setTimeout(callback, delay) and it should:

  1. Reset the timer if delay changes.
  2. DO NOT reset the timer if only callback changes.
export function useTimeout(callback: () => void, delay: number) {
const callbackFn = React.useRef(null)
callbackFn.current = callback

React.useEffect(() => {
const timeoutId = setTimeout(() => {
if (callbackFn.current) {
callbackFn.current()
}
}, delay)

return () => {
clearTimeout(timeoutId)
}
}, [delay]) // Only re-run if delay changes
}

Usage

import { useState } from 'react'

export default function Component() {
const [visible, setVisible] = useState(true)

const hide = () => setVisible(false)

useTimeout(hide, 5000)

return (
<div>
<p>
{visible
? "I'm visible for 5000ms"
: 'You can no longer see this content'}
</p>
</div>
)
}

References

Other Articles

https://medium.com/@lama.ibrahim96/whats-the-difference-between-for-in-and-object-keys-6d872a3d526a

Thanks for reading, if you learned something from this article please Follow Me 😊 I will be posting more content about React and Javascript.

--

--

Lama Ibrahim

Senior Software Engineer 😎 with a pretty good Experience in the FrontEnd Web Development, and ReactJs.