use Temporary State (React)
Temporarily set a piece of state, and have it cleared after a timeout. Useful for things like a copy or download button, to temporarily show that the action has been triggered.
typescriptjavascript
import type { SetStateAction, Dispatch } from 'react'
import { useState, useCallback, useRef, useEffect } from 'react'
export const useTemporaryState = <State>(
initial: State | (() => State),
timeout = 2000
): [State, Dispatch<SetStateAction<State>>] => {
const [state, _setState] = useState<State>(initial)
const timeoutRef = useRef<ReturnType<typeof setTimeout>>()
const initialValueRef = useRef(initial)
useEffect(() => {
initialValueRef.current = initial
}, [initial])
const setState: typeof _setState = useCallback(
async valueOrUpdater => {
_setState(valueOrUpdater)
if (timeoutRef.current) clearTimeout(timeoutRef.current)
timeoutRef.current = setTimeout(
() => _setState(initialValueRef.current),
timeout
)
},
[timeout]
)
return [state, setState]
}
import { useState, useCallback, useRef, useEffect } from 'react'
export const useTemporaryState = (initial, timeout = 2000) => {
const [state, _setState] = useState(initial)
const timeoutRef = useRef()
const initialValueRef = useRef(initial)
useEffect(() => {
initialValueRef.current = initial
}, [initial])
const setState = useCallback(
async valueOrUpdater => {
_setState(valueOrUpdater)
if (timeoutRef.current) clearTimeout(timeoutRef.current)
timeoutRef.current = setTimeout(
() => _setState(initialValueRef.current),
timeout
)
},
[timeout]
)
return [state, setState]
}
Info
-
We use
useCallback
, so that if you follow hook rules and use thecopy
function inside auseEffect
or anotheruseCallback
, you won't have code (re-)running unnecessarily. -
We need the combination of
useRef
anduseEffect
to store the initial value as it ensures thesetState
callback isn't re-created if it changes.
Demo
Example
- See useCopy