I wrote a react hook to never again have to deal with timeouts.
works just like React.useState():
New answer
const [showLoading, setShowLoading] = useTimeoutState(false)
// sets loading to true for 1000ms, then back to false
setShowLoading(true, { timeout: 1000})
export const useTimeoutState = <T>(
defaultState: T
): [T, (action: SetStateAction<T>, opts?: { timeout: number }) => void] => {
const [state, _setState] = useState<T>(defaultState);
const [currentTimeoutId, setCurrentTimeoutId] = useState<
NodeJS.Timeout | undefined
>();
const setState = useCallback(
(action: SetStateAction<T>, opts?: { timeout: number }) => {
if (currentTimeoutId != null) {
clearTimeout(currentTimeoutId);
}
_setState(action);
const id = setTimeout(() => _setState(defaultState), opts?.timeout);
setCurrentTimeoutId(id);
},
[currentTimeoutId, defaultState]
);
return [state, setState];
};
Old answer
const [showLoading, setShowLoading] = useTimeoutState(false, {timeout: 5000})
// will set show loading after 5000ms
setShowLoading(true)
// overriding and timeouts after 1000ms
setShowLoading(true, { timeout: 1000})
Setting multiple states will refresh the timeout and it will timeout after the same ms that the last setState
set.
Vanilla js (not tested, typescript version is):
import React from "react"
// sets itself automatically to default state after timeout MS. good for setting timeouted states for risky requests etc.
export const useTimeoutState = (defaultState, opts) => {
const [state, _setState] = React.useState(defaultState)
const [currentTimeoutId, setCurrentTimeoutId] = React.useState()
const setState = React.useCallback(
(newState: React.SetStateAction, setStateOpts) => {
clearTimeout(currentTimeoutId) // removes old timeouts
newState !== state && _setState(newState)
if (newState === defaultState) return // if already default state, no need to set timeout to set state to default
const id = setTimeout(
() => _setState(defaultState),
setStateOpts?.timeout || opts?.timeout
)
setCurrentTimeoutId(id)
},
[currentTimeoutId, state, opts, defaultState]
)
return [state, setState]
}
Typescript:
import React from "react"
interface IUseTimeoutStateOptions {
timeout?: number
}
// sets itself automatically to default state after timeout MS. good for setting timeouted states for risky requests etc.
export const useTimeoutState = <T>(defaultState: T, opts?: IUseTimeoutStateOptions) => {
const [state, _setState] = React.useState<T>(defaultState)
const [currentTimeoutId, setCurrentTimeoutId] = React.useState<number | undefined>()
// todo: change any to React.setStateAction with T
const setState = React.useCallback(
(newState: React.SetStateAction<any>, setStateOpts?: { timeout?: number }) => {
clearTimeout(currentTimeoutId) // removes old timeouts
newState !== state && _setState(newState)
if (newState === defaultState) return // if already default state, no need to set timeout to set state to default
const id = setTimeout(
() => _setState(defaultState),
setStateOpts?.timeout || opts?.timeout
) as number
setCurrentTimeoutId(id)
},
[currentTimeoutId, state, opts, defaultState]
)
return [state, setState] as [
T,
(newState: React.SetStateAction<T>, setStateOpts?: { timeout?: number }) => void
]
}```
useState
is a proposed update to ReactJS's API – Lurie