Skip to content

Instantly share code, notes, and snippets.

@Misaka-0x447f
Last active May 25, 2024 12:30
Show Gist options
  • Save Misaka-0x447f/18cc14320abf8da37201863865df5047 to your computer and use it in GitHub Desktop.
Save Misaka-0x447f/18cc14320abf8da37201863865df5047 to your computer and use it in GitHub Desktop.
useHybridState.ts
import { isFunction } from 'lodash-es'
import {
useCallback,
useRef,
useState,
useMemo,
Dispatch,
SetStateAction,
DependencyList,
useEffect
} from 'react'
export const useHybridState = <T>(initialValue?: T | undefined) => {
const [state, setState] = useState(initialValue as T)
const ref = useRef(initialValue)
const set = useCallback((val: T) => {
if (isFunction(val)) {
ref.current = val(ref.current)
} else {
ref.current = val
}
setState(val)
}, [])
const refBehindProxy = useMemo(
() =>
new Proxy(ref, {
set: () => {
throw new Error(
"Do not change value of this ref, it's readonly. Instead, use the set method."
)
}
}),
[]
)
return [state, set, refBehindProxy] as [
typeof state,
Dispatch<SetStateAction<T>>,
{
readonly current: T
}
]
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const useHybridUpdateState = <T>(initialValue: T | undefined) => {
const [state, setState, stateRef] = useHybridState<T>(initialValue)
const updateState = (partialState: Partial<T>) => {
setState((prevState) => Object.assign({}, prevState, partialState))
}
return [state, updateState, stateRef, setState] as const
}
export const useHybridMemo = <T>(factory: () => T, deps: DependencyList | undefined) => {
const [state, setState, stateRef] = useHybridState<T>()
useEffect(() => {
setState(factory())
}, deps)
return [state, stateRef] as const
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment