โจ Watch on YouTube | ๐ GitHub | ๐ฎ Demo
There is a useful UX pattern of displaying a temporary message in response to a user's action. Recently, I had to add such a timeout message to my project and I thought it might be beneficial to package it into an abstract component and share it with you.
How To Use the TimeoutMessage Component
Here's how you can use it to display a temporary message after a user updates their profile.
If one of the values in the deps
array has changed, the component will call the render
function and display the message for five seconds.
<TimeoutMessage
deps={[isAnonymous, name, country]}
timeout={5000}
render={() => <UpdateProfileMessage />}
/>
TimeoutMessage Component
The TimeoutMessage
component is a simple wrapper around the useTimeoutState
and useEffectOnDependencyChange
hooks. Instead of using those hooks directly, we achieve a more declarative and compact code using the TimeoutMessage
component.
import { DependencyList, ReactNode } from "react"
import { useEffectOnDependencyChange } from "../hooks/useEffectOnDependencyChange"
import { useTimeoutState } from "../hooks/useTimeoutState"
interface TimeoutMessageProps {
render: () => ReactNode
timeout: number
deps: DependencyList
}
export const TimeoutMessage = ({
render,
timeout,
deps,
}: TimeoutMessageProps) => {
const [shouldShowMessage, setShouldShowMessage] = useTimeoutState(
false,
timeout
)
useEffectOnDependencyChange(() => {
setShouldShowMessage(true)
}, deps)
return shouldShowMessage ? <>{render()}</> : null
}
useTimeoutState Hook
The useTimeoutState
hook is similar to the useState
hook, but it resets the state to the default value after a given timeout.
import { useEffect, useState } from "react"
export const useTimeoutState = <T,>(
defaultValue: T,
timeoutDuration: number
) => {
const [state, setState] = useState(defaultValue)
useEffect(() => {
if (state !== defaultValue) {
const timeout = setTimeout(() => {
setState(defaultValue)
}, timeoutDuration)
return () => clearTimeout(timeout)
}
}, [state, defaultValue, setState, timeoutDuration])
return [state, setState] as const
}
In our TimeoutMessage
component, we use useTimeoutState
to monitor the message visibility. We set shouldShowMessage
to true
when the deps
change, and the useTimeoutState
hook will reset it back to false
after the specified timeout.
useEffectOnDependencyChange Hook
The useEffectOnDependencyChange
hook is the same as the useEffect
hook. However, it only calls the effect
function when one of the values in the deps
array has changed, and will not call it on the initial render.
import { DependencyList, useEffect, useRef } from "react"
export const useEffectOnDependencyChange = (
effect: () => void,
deps: DependencyList
) => {
const prevDeps = useRef(deps)
useEffect(() => {
const hasDepsChanged = !prevDeps.current.every((dep, i) => dep === deps[i])
if (hasDepsChanged) {
effect()
prevDeps.current = deps
}
}, deps)
}
Top comments (0)