You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
36 lines
1.3 KiB
36 lines
1.3 KiB
import type { Dispatch } from 'react'
|
|
import { useCallback, useState } from 'react'
|
|
|
|
import isFunction from 'lodash/isFunction'
|
|
|
|
type Updater<S> = (prevState: S) => Partial<S>
|
|
const isUpdater = <S extends object>(value: any): value is Updater<S> => (
|
|
isFunction(value)
|
|
)
|
|
|
|
/**
|
|
* Дженерик для создания типа сеттера, получает тип стейта как параметр
|
|
*/
|
|
export type SetPartialState<S> = Dispatch<Partial<S> | Updater<S>>
|
|
|
|
/**
|
|
* Хук на основе useState с возможносьтю мержа стейта как в классовых компонентах
|
|
*/
|
|
export const useObjectState = <S extends object>(initialState: S) => {
|
|
const [state, setState] = useState(initialState)
|
|
|
|
const setPartialState: SetPartialState<S> = useCallback((newStateOrUpdater) => {
|
|
setState((oldState) => {
|
|
const newState = isUpdater(newStateOrUpdater)
|
|
? newStateOrUpdater(oldState)
|
|
: newStateOrUpdater
|
|
// если updater функция вернула старый стейт то и здесь
|
|
// возвращаем тот же стейт чтобы избежать лишнего ререндера
|
|
if (newState === oldState) return oldState
|
|
|
|
return { ...oldState, ...newState }
|
|
})
|
|
}, [])
|
|
|
|
return [state, setPartialState] as const
|
|
}
|
|
|