import { Dispatch, SetStateAction, useCallback } from 'react'
import { isFunction } from '@spicy-hooks/utils'
/**
* Treats a property of an object as a stand-alone state.
*
* This hook can be used to access a property of a complex state in a transparent way.
* The returned value and setter can be treated in the same way as the ones
* returned by `useState` directly.
*
* **Usage example:**
*
* ```jsx
* const [object, setObject] = useState({ a: 'a', b: 1 })
* const [b, setB] = useProperty(object, setObject, 'b')
* ...
* return (
*
* Current value of B: {b}
*
*
* )
* ```
* Note that the above shows very simple case in which it would probably be better to allocate two different states,
* there are situations though when you either cannot influence this (the object comes from above)
* or it is beneficial to use the whole object as an atomic unit.
*
* @param object complex state value
* @param updateObject function that updates the complex state by passing the current state into the callback and expecting a modified state back (just like `setState(prev => prev + 1)`
* @param property name of the property of the `object` that is to be managed
* @typeParam O type of the complex state
* @typeParam P the actual managed property of `O`
* @returns the same output as ˙useState˙, but scoping everything to the provided property
* @category Hook
*/
export function useProperty (
object: O,
updateObject: Dispatch<(prevState: O) => O>,
property: P
): [O[P], Dispatch>] {
const setState = useCallback(
(value: SetStateAction) =>
updateObject(prevCompositeState => {
const previousValue = prevCompositeState[property]
const newValue = isFunction(value)
? value(previousValue)
: value
if (newValue === previousValue) {
return prevCompositeState
}
return {
...prevCompositeState,
[property]: newValue
}
}),
[updateObject, property]
)
return [object[property], setState]
}