First, thank you so many guys for supporting the first part. In this article, I've come up with 15 more react custom hooks that can be helpful for you whether you are building a small or large project.
NOTE: I've used some of the custom hooks here, which I explained in the first part. If you haven't read the first part, you might have problems understanding the code. That's why I recommend reading the first part first. You can find the article here: first part
Table of contents
- useMediaQuery
- useGeolocation
- useStateWithValidation
- useSize
- useEffectOnce
- useClickOutside
- useDarkMode
- useCopyToClipboard
- useCookie
- useTranslation
- useOnlineStatus
- useRenderCount
- useDebugInformation
- useHover
- useLongPress
1. useMediaQuery
import { useState, useEffect } from "react"
import useEventListener from "../13-useEventListener/useEventListener"
export default function useMediaQuery(mediaQuery) {
const [isMatch, setIsMatch] = useState(false)
const [mediaQueryList, setMediaQueryList] = useState(null)
useEffect(() => {
const list = window.matchMedia(mediaQuery)
setMediaQueryList(list)
setIsMatch(list.matches)
}, [mediaQuery])
useEventListener("change", e => setIsMatch(e.matches), mediaQueryList)
return isMatch
}
useMediaQuery
is a custom React hook that allows a component to check if a specific media query matches the current viewport and keep track of the match status. In addition, it uses the built-in useState
and useEffect
hooks from the React library and a custom hook called useEventListener
(this hook is exaplined in the first part) that allows a component to add an event listener to a specific DOM element and execute a callback function when the event occurs.
The hook takes one argument:
- mediaQuery is a string representing the media query to check for.
The useEffect
hook creates a MediaQueryList object from the media query and sets the initial match status.
The useEventListener
hook adds a change event listener to the MediaQueryList object and updates the match status when the event occurs.
It returns a boolean value isMatch
that indicates whether the media query matches the current viewport.
Here is an example of how you can use this hook:
import useMediaQuery from "./useMediaQuery"
export default function MediaQueryComponent() {
const isLarge = useMediaQuery("(min-width: 200px)")
return <div>Large: {isLarge.toString()}</div>
}
This hook can be helpful in situations where you want to change the layout or behavior of a component based on the current viewport, such as adapting the layout to different screen sizes or device types.
2. useGeolocation
import { useState, useEffect } from "react"
export default function useGeolocation(options) {
const [loading, setLoading] = useState(true)
const [error, setError] = useState()
const [data, setData] = useState({})
useEffect(() => {
const successHandler = e => {
setLoading(false)
setError(null)
setData(e.coords)
}
const errorHandler = e => {
setError(e)
setLoading(false)
}
navigator.geolocation.getCurrentPosition(
successHandler,
errorHandler,
options
)
const id = navigator.geolocation.watchPosition(
successHandler,
errorHandler,
options
)
return () => navigator.geolocation.clearWatch(id)
}, [options])
return { loading, error, data }
}
useGeolocation
is a custom React Hook that allows a component to access the browser's geolocation API and keep track of the device's current position. It uses the React library's built-in useState and useEffect hooks.
The hook takes one optional argument: options is an object that allows you to configure the geolocation API, such as the maximum age of the cached position and the timeout before the error callback is called.
The useEffect hook calls the geolocation API and the getCurrentPosition
and watchPosition
methods. The callback passed to these methods updates the state with the current position data and set the loading and error state accordingly.
It returns an object containing three properties:
loading is a boolean indicating if the geolocation data is currently being fetched.
error is an error object if there is an error fetching the geolocation data.
data is an object containing the latitude, longitude, accuracy, altitude, and other properties of the device's current position.
Here is an exmaple of how you can use this hook:
import useGeolocation from "./useGeolocation"
export default function GeolocationComponent() {
const {
loading,
error,
data: { latitude, longitude },
} = useGeolocation()
return (
<>
<div>Loading: {loading.toString()}</div>
<div>Error: {error?.message}</div>
<div>
{latitude} x {longitude}
</div>
</>
)
}
This hook can be helpful in situations where you want to access the user's location data, such as displaying nearby locations on a map or providing location-based services.
3. useStateWithValidation
import { useState, useCallback } from "react"
export default function useStateWithValidation(validationFunc, initialValue) {
const [state, setState] = useState(initialValue)
const [isValid, setIsValid] = useState(() => validationFunc(state))
const onChange = useCallback(
nextState => {
const value =
typeof nextState === "function" ? nextState(state) : nextState
setState(value)
setIsValid(validationFunc(value))
},
[validationFunc]
)
return [state, onChange, isValid]
}
useStateWithValidation
is a custom React Hook that allows a component to keep track of a state value and validate the state value on each change. It uses the React library's built-in useState
and useCallback
hooks.
The hook takes two arguments:
validationFunc is a function that will be called with the new state value on each state change. It should return a boolean indicating whether the state value is valid.
initialValue is the initial value of the state.
The useState
hook is used to initialize the state with the provided initial value and the validation state with the result of the validation function called on the initial value.
The useCallback
hook is used to create a callback function that updates the state and validation state with the new value passed to it.
It returns an array containing three properties:
state is the current state value.
onChange is a callback function that should be passed to an input/form element and will update the state and validation state with the new value.
isValid is a boolean indicating whether the state value is valid.
Here is an example of how you can use this hook:
import useStateWithValidation from "./useStateWithValidation"
export default function StateWithValidationComponent() {
const [username, setUsername, isValid] = useStateWithValidation(
name => name.length > 5,
""
)
return (
<>
<div>Valid: {isValid.toString()}</div>
<input
type="text"
value={username}
onChange={e => setUsername(e.target.value)}
/>
</>
)
}
This hook can be helpful in situations where you want to validate the input values of a form or validate any other state value before performing a specific action.
4. useSize
import { useState } from "react"
import { useEffect } from "react/cjs/react.development"
export default function useSize(ref) {
const [size, setSize] = useState({})
useEffect(() => {
if (ref.current == null) return
const observer = new ResizeObserver(([entry]) => setSize(entry.contentRect))
observer.observe(ref.current)
return () => observer.disconnect()
}, [])
return size
}
useSize
is a custom React Hook that allows a component to track the size of an element, passed as a reference. It uses the React library's built-in useState
and useEffect
hooks.
The hook takes one argument, ref
, which refers to the element you want to track its size.
The useState
hook initializes an empty object as the size state.
The useEffect
hook is used to observe the element's size passed by reference. First, it creates a new instance of ResizeObserver, a built-in browser API that allows tracking the size of an element. Then, it invokes the callback function passed to it, which updates the size state with the contentRect
property of the entry passed to it.
It returns an object containing the element's size with properties: width
, height
, x
, y
, etc.
Here is an example of how you can use this hook:
import { useRef } from "react"
import useSize from "./useSize"
export default function SizeComponent() {
const ref = useRef()
const size = useSize(ref)
return (
<>
<div>{JSON.stringify(size)}</div>
<textarea ref={ref}></textarea>
</>
)
}
This hook can be helpful in situations where you want to track the size of an element and change the component behavior or layout based on its size.
5. useEffectOnce
import { useEffect } from "react"
export default function useEffectOnce(cb) {
useEffect(cb, [])
}
useEffectOnce
is a custom React Hook that allows a component to run an effect only once. It is a simpler version of the built-in useEffect
hook, enabling you to specify how often an effect should run.
The hook takes one argument, cb
, which is the callback function that contains the logic of the effect.
It calls the built-in useEffect
hook and passes the cb function as the first argument and an empty array as the second argument.
An empty dependency array is passed to the useEffect
hook, which means that the effect will only run once on the initial render.
Here is an example of how you can use this hook:
import { useState } from "react"
import useEffectOnce from "./useEffectOnce"
export default function EffectOnceComponent() {
const [count, setCount] = useState(0)
useEffectOnce(() => alert("Hi"))
return (
<>
<div>{count}</div>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</>
)
}
This hook can be helpful when you want to run an effect, such as initializing a library, only once in a component, instead of running it on every render.
6. useClickOutside
import useEventListener from "../13-useEventListener/useEventListener"
export default function useClickOutside(ref, cb) {
useEventListener(
"click",
e => {
if (ref.current == null || ref.current.contains(e.target)) return
cb(e)
},
document
)
}
useClickOutside
is a custom hook that listens for a click event on the document, and when a click happens, it checks if the event target is within the current ref. If it is not, it will call the callback function passed in. This hook uses useEventListener
(this hook is exaplined in the first part) that allows a component to add an event listener to a specific DOM element and execute a callback function when the event occurs.
Here is an exmaple of how you can use this hook:
import { useRef, useState } from "react"
import useClickOutside from "./useClickOutside"
export default function ClickOutsideComponent() {
const [open, setOpen] = useState(false)
const modalRef = useRef()
useClickOutside(modalRef, () => {
if (open) setOpen(false)
})
return (
<>
<button onClick={() => setOpen(true)}>Open</button>
<div
ref={modalRef}
style={{
display: open ? "block" : "none",
backgroundColor: "blue",
color: "white",
width: "100px",
height: "100px",
position: "absolute",
top: "calc(50% - 50px)",
left: "calc(50% - 50px)",
}}
>
<span>Modal</span>
</div>
</>
)
}
This hook can be helpful when you need to detect clicks outside of a specific element, such as when creating a modal component, and you want to close the modal when the user clicks outside of it.
7. useDarkMode
import { useEffect } from "react"
import useMediaQuery from "../16-useMediaQuery/useMediaQuery"
import { useLocalStorage } from "../8-useStorage/useStorage"
export default function useDarkMode() {
const [darkMode, setDarkMode] = useLocalStorage("useDarkMode")
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)")
const enabled = darkMode ?? prefersDarkMode
useEffect(() => {
document.body.classList.toggle("dark-mode", enabled)
}, [enabled])
return [enabled, setDarkMode]
}
This hook allows for a dark mode feature in a React application. It uses the custom useLocalStorage(explained in the first part) hook to get and set the dark mode preference in local storage and the useMediaQuery
hook to check if the user's device or browser prefers dark mode.
The hook uses useEffect to toggle the dark-mode class on the document body based on the value of the enabled variable, which is determined by the user's preference in local storage or the device's preference for dark mode. This allows the application's styles to be adjusted when the dark mode is enabled. Finally, the hook returns a tuple containing the current state of the dark way and the setDarkMode function, which is used to toggle the dark mode.
Here is an example of how you can use this hook:
import useDarkMode from "./useDarkMode"
import "./body.css"
export default function DarkModeComponent() {
const [darkMode, setDarkMode] = useDarkMode()
return (
<button
onClick={() => setDarkMode(prevDarkMode => !prevDarkMode)}
style={{
border: `1px solid ${darkMode ? "white" : "black"}`,
background: "none",
color: darkMode ? "white" : "black",
}}
>
Toggle Dark Mode
</button>
)
}
body.dark-mode {
background-color: #333;
}
8. useCopyToClipboard
import { useState } from "react"
import copy from "copy-to-clipboard"
export default function useCopyToClipboard() {
const [value, setValue] = useState()
const [success, setSuccess] = useState()
const copyToClipboard = (text, options) => {
const result = copy(text, options)
if (result) setValue(text)
setSuccess(result)
}
return [copyToClipboard, { value, success }]
}
useCopyToClipboard
allows you to copy text to the clipboard. First, the hook uses the useState
hook to track the text being copied and whether the copy was successful. Then, it exports a single function, copyToClipboard
, which, when called, will copy the provided text to the clipboard.
The hook also has two state values, value and success, that can be destructured from the hook's return value. The value keeps track of the text copied to the clipboard, and success keeps track of whether the copy was successful.
Here is an example of how you can use this hook:
import useCopyToClipboard from "./useCopyToClipboard"
export default function CopyToClipboardComponent() {
const [copyToClipboard, { success }] = useCopyToClipboard()
return (
<>
<button onClick={() => copyToClipboard("This was copied")}>
{success ? "Copied" : "Copy Text"}
</button>
<input type="text" />
</>
)
}
This hook can be helpful in situations where you want to allow the user to copy text from your application to the clipboard. For example, you might use it to enable the user to copy a coupon code or a referral link to their clipboard.
9. useCookie
import { useState, useCallback } from "react"
import Cookies from "js-cookie"
export default function useCookie(name, defaultValue) {
const [value, setValue] = useState(() => {
const cookie = Cookies.get(name)
if (cookie) return cookie
Cookies.set(name, defaultValue)
return defaultValue
})
const updateCookie = useCallback(
(newValue, options) => {
Cookies.set(name, newValue, options)
setValue(newValue)
},
[name]
)
const deleteCookie = useCallback(() => {
Cookies.remove(name)
setValue(null)
}, [name])
return [value, updateCookie, deleteCookie]
}
useCookie
is a custom hook that uses the js-cookie
library to interact with cookies. It allows you to get
, set
, and delete
a specific cookie by name.
The hook takes two parameters: the cookie's name and a default value.
When the hook is first called, it attempts to get the value of the cookie. It will set the cookie with the default value if it's not present. The hook returns an array with three elements: the value of the cookie, a function for updating the cookie, and a function for deleting the cookie.
The updateCookie function takes a new value and options to pass to the js-cookie
set method. The deleteCookie function takes no parameter and deletes the cookie using the js-cookie remove method.
Here is an example of how you can use this hook:
import useCookie from "./useCookie"
export default function CookieComponent() {
const [value, update, remove] = useCookie("name", "John")
return (
<>
<div>{value}</div>
<button onClick={() => update("Sally")}>Change Name To Sally</button>
<button onClick={remove}>Delete Name</button>
</>
)
}
This hook can be used in any component where you must interact with cookies, such as storing user preferences or authentication tokens.
10. useTranslation
{
"hi": "Hello",
"bye": "Goodbye",
"nested": { "value": "Test" }
}
export * as en from "./en.json"
export * as sp from "./sp.json"
{
"hi": "Hola"
}
import { useLocalStorage } from "../8-useStorage/useStorage"
import * as translations from "./translations"
export default function useTranslation() {
const [language, setLanguage] = useLocalStorage("language", "en")
const [fallbackLanguage, setFallbackLanguage] = useLocalStorage(
"fallbackLanguage",
"en"
)
const translate = key => {
const keys = key.split(".")
return (
getNestedTranslation(language, keys) ??
getNestedTranslation(fallbackLanguage, keys) ??
key
)
}
return {
language,
setLanguage,
fallbackLanguage,
setFallbackLanguage,
t: translate,
}
}
function getNestedTranslation(language, keys) {
return keys.reduce((obj, key) => {
return obj?.[key]
}, translations[language])
}
This custom hook provides an easy way to add internationalization (i18n) to a React application. It uses the useLocalStorage
(explained in first part) hook to keep the language and fallback language settings in the browser's local storage so that the user's language preference can be remembered between sessions.
In addition, the hook exports an object with the following properties:
Language: The currently selected language.
setLanguage: A function that allows you to change the selected language.
fallbackLanguage: The fallback language that will be used if a translation is not found in the selected language.
setFallbackLanguage: A function that allows you to change the fallback language.
t: A function that takes a key as an argument and returns the corresponding translation in the selected or fallback language, if available.
The hook uses translations from the ./translations file, which should contain an object for each language that maps keys to translations. If a translation is not found in the selected language, the hook will try to find it in the fallback language. It will return the key itself if it's not found in either.
Here is an example of how you can use this hook:
import useTranslation from "./useTranslation"
export default function TranslationComponent() {
const { language, setLanguage, setFallbackLanguage, t } = useTranslation()
return (
<>
<div>{language}</div>
<div>{t("hi")}</div>
<div>{t("bye")}</div>
<div>{t("nested.value")}</div>
<button onClick={() => setLanguage("sp")}>Change To Spanish</button>
<button onClick={() => setLanguage("en")}>Change To English</button>
<button onClick={() => setFallbackLanguage("sp")}>Change FB Lang</button>
</>
)
}
This hook can be used in any React application where you need to support multiple languages. For example, you can use the t
function to translate strings in your components and use the language and fallbackLanguage properties to change the language of your application.
11. useOnlineStatus
import { useState } from "react"
import useEventListener from "../13-useEventListener/useEventListener"
export default function useOnlineStatus() {
const [online, setOnline] = useState(navigator.onLine)
useEventListener("online", () => setOnline(navigator.onLine))
useEventListener("offline", () => setOnline(navigator.onLine))
return online
}
This hook detects the online status of the user. It uses the useState hook from React to store the current online status and the useEventListener(explained in the first part) hook to listen for changes in the user's online status.
The hook uses the navigator.onLine property to check the current online status when the component loads and sets the online state variable accordingly. Then, it uses the useEventListener hook to listen for changes in the online and offline events, which are fired when the user goes online or offline.
When either of these events occurs, it updates the online state variable by calling setOnline
(navigator.onLine). The hook then returns the current value of the online state variable, which can be used in the component to determine whether the user is currently online or offline.
Here is an example of how you can use this hook:
import useOnlineStatus from "./useOnlineStatus"
export default function OnlineStatusComponent() {
const online = useOnlineStatus()
return <div>{online.toString()}</div>
}
You can use this hook in any component where you need to know whether the user is currently online or offline, for example, to show a message to the user if they are offline or to disable certain functionality.
12. useRenderCount
import { useEffect, useRef } from "react"
export default function useRenderCount() {
const count = useRef(1)
useEffect(() => count.current++)
return count.current
}
This custom Hook allows you to track the number of times a component has been rendered. It uses the useEffect
and useRef
hooks from React. The useRef
hook creates a ref with an initial value of 1, which will keep track of the number of renders. The useEffect
hook is then used to increment the count ref on every render. Finally, the Hook returns the current count, which can be displayed in the component.
Here is an example of how you can use this hook:
import useRenderCount from "./useRenderCount"
import useToggle from "../1-useToggle/useToggle"
export default function RenderCountComponent() {
const [boolean, toggle] = useToggle(false)
const renderCount = useRenderCount()
return (
<>
<div>{boolean.toString()}</div>
<div>{renderCount}</div>
<button onClick={toggle}>Toggle</button>
</>
)
}
13. useDebugInformation
import { useEffect, useRef } from "react"
import useRenderCount from "../27-useRenderCount/useRenderCount"
export default function useDebugInformation(componentName, props) {
const count = useRenderCount()
const changedProps = useRef({})
const previousProps = useRef(props)
const lastRenderTimestamp = useRef(Date.now())
const propKeys = Object.keys({ ...props, ...previousProps })
changedProps.current = propKeys.reduce((obj, key) => {
if (props[key] === previousProps.current[key]) return obj
return {
...obj,
[key]: { previous: previousProps.current[key], current: props[key] },
}
}, {})
const info = {
count,
changedProps: changedProps.current,
timeSinceLastRender: Date.now() - lastRenderTimestamp.current,
lastRenderTimestamp: lastRenderTimestamp.current,
}
useEffect(() => {
previousProps.current = props
lastRenderTimestamp.current = Date.now()
console.log("[debug-info]", componentName, info)
})
return info
}
useDebugInformation
is a custom React hook that logs information about a component's render. It uses two arguments, a componentName
string, and a props
object.
The hook uses the useEffect
and useRef
hooks from the React library to track the number of renders, changes in props, and the time since the last render. When the component re-renders, the hook will log information about the component's render, including the component name, render count, changed props, time since the last render, and final render timestamp.
Here is an example of how you can use this hook:
import useDebugInformation from "./useDebugInformation"
import useToggle from "../1-useToggle/useToggle"
import { useState } from "react"
export default function DebugInformationComponent() {
const [boolean, toggle] = useToggle(false)
const [count, setCount] = useState(0)
return (
<>
<ChildComponent boolean={boolean} count={count} />
<button onClick={toggle}>Toggle</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>
Increment
</button>
</>
)
}
function ChildComponent(props) {
const info = useDebugInformation("ChildComponent", props)
return (
<>
<div>{props.boolean.toString()}</div>
<div>{props.count}</div>
<div>{JSON.stringify(info, null, 2)}</div>
</>
)
}
The hook can be used in any functional component to log this information for debugging or monitoring purposes.
Note that useToggle
hook is being used here, which I explained in the first part.
14. useHover
import { useState } from "react"
import useEventListener from "../13-useEventListener/useEventListener"
export default function useHover(ref) {
const [hovered, setHovered] = useState(false)
useEventListener("mouseover", () => setHovered(true), ref.current)
useEventListener("mouseout", () => setHovered(false), ref.current)
return hovered
}
This hook lets you track whether an element is currently being hovered by the user's mouse. The hook takes a single argument, ref, which refers to the element you want to track hover events for.
The hook uses the useState
hook to track whether the element is currently being hovered over and the useEventListener
(explained in first part) hook to listen for mouseover
and mouseout
events on the element. When a mouseover event occurs, the state is true, indicating that the element is being hovered over. When a mouseout event occurs, the state is set to false, indicating that the element is no longer being hovered over.
Finally, the hook returns the current hovered state, which can be used to control the appearance or behavior of the element. For example, this hook can track whether the user is hovering over a specific element on the page.
Here is an example of how you can use this hook:
import { useRef } from "react"
import useHover from "./useHover"
export default function HoverComponent() {
const elementRef = useRef()
const hovered = useHover(elementRef)
return (
<div
ref={elementRef}
style={{
backgroundColor: hovered ? "blue" : "red",
width: "100px",
height: "100px",
position: "absolute",
top: "calc(50% - 50px)",
left: "calc(50% - 50px)",
}}
/>
)
}
15. useLongPress
import useEventListener from "../13-useEventListener/useEventListener"
import useTimeout from "../2-useTimeout/useTimeout"
import useEffectOnce from "../20-useEffectOnce/useEffectOnce"
export default function useLongPress(ref, cb, { delay = 250 } = {}) {
const { reset, clear } = useTimeout(cb, delay)
useEffectOnce(clear)
useEventListener("mousedown", reset, ref.current)
useEventListener("touchstart", reset, ref.current)
useEventListener("mouseup", clear, ref.current)
useEventListener("mouseleave", clear, ref.current)
useEventListener("touchend", clear, ref.current)
}
useLongPress
hook allows you to detect a long press on a specific element using a combination of event listeners custom hook(which I've already explained) and the useTimeout
hook. When the element specified by the ref is pressed down, it starts a timer with the specified delay, and when the press is released before the delay expires, the timer is cleared. However, if the press is held for longer than the delay, the callback function passed in is executed.
Here is an example of how you can use this hook:
import { useRef } from "react"
import useLongPress from "./useLongPress"
export default function LongPressComponent() {
const elementRef = useRef()
useLongPress(elementRef, () => alert("Long Press"))
return (
<div
ref={elementRef}
style={{
backgroundColor: "red",
width: "100px",
height: "100px",
position: "absolute",
top: "calc(50% - 50px)",
left: "calc(50% - 50px)",
}}
/>
)
}
This hook can be used in any component where you want to detect a long press event on a specific element, for example, a button.
Conclusion
In conclusion, custom hooks provide a powerful and flexible way for developers to share logic across their React components. They allow for easy reuse and organization of code, making it easier to reason about and manage the state and behavior of an element. In addition, with hooks, developers can now abstract away complex logic, making their components more focused and easier to test. The hooks provided in this article are just a few examples of what can be accomplished with hooks, and there are many more possibilities to explore. The growing popularity of hooks in the React community is a testament to their utility. They will likely be an essential tool for developers in their React projects.
Top comments (6)
react-helper-hooks library
github.com/punitsonime/react-helpe...
npmjs.com/package/react-helper-hooks
Wow, did you make it?
Yes brother
That's great man. Cheers😊👌
Dude you just saved us a looooot of googleing and Docs reading, thank you 🫡
No problem mate, glad you liked it😊