UNPKG

@hakuna-matata-ui/hooks

Version:
68 lines (57 loc) 1.81 kB
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>>] }