@hakuna-matata-ui/hooks
Version:
React hooks for Chakra components
68 lines (57 loc) • 1.81 kB
text/typescript
import { runIfFn } from "@hakuna-matata-ui/utils"
import * as React from "react"
import { useCallbackRef } from "./use-callback-ref"
export function useControllableProp<T>(prop: T | undefined, state: T) {
const isControlled = prop !== undefined
const value = isControlled && typeof prop !== "undefined" ? prop : state
return [isControlled, value] as const
}
export interface UseControllableStateProps<T> {
/**
* The value to used in controlled mode
*/
value?: T
/**
* The initial value to be used, in uncontrolled mode
*/
defaultValue?: T | (() => T)
/**
* The callback fired when the value changes
*/
onChange?: (value: T) => void
/**
* The function that determines if the state should be updated
*/
shouldUpdate?: (prev: T, next: T) => boolean
}
/**
* React hook for using controlling component state.
* @param props
*/
export function useControllableState<T>(props: UseControllableStateProps<T>) {
const {
value: valueProp,
defaultValue,
onChange,
shouldUpdate = (prev, next) => prev !== next,
} = props
const onChangeProp = useCallbackRef(onChange)
const shouldUpdateProp = useCallbackRef(shouldUpdate)
const [valueState, setValue] = React.useState(defaultValue as T)
const isControlled = valueProp !== undefined
const value = isControlled ? (valueProp as T) : valueState
const updateValue = React.useCallback(
(next: React.SetStateAction<T>) => {
const nextValue = runIfFn(next, value)
if (!shouldUpdateProp(value, nextValue)) {
return
}
if (!isControlled) {
setValue(nextValue)
}
onChangeProp(nextValue)
},
[isControlled, onChangeProp, value, shouldUpdateProp],
)
return [value, updateValue] as [T, React.Dispatch<React.SetStateAction<T>>]
}