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.
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 />}
/>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
}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.
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)
}