useObjectState
한 컴포넌트에서 쓰는 State들을 하나의 hooks로 관리할 수 있도록 만든 훅스입니다.
Copy Code
hooks/use-object-state.tsx
'use client'
import { useCallback, useEffect, useRef, useState } from 'react'
import type { ChangeEvent } from 'react'
function useObjectState<T>(
initialObject: T
): [
T,
(obj: Partial<T>, callback?: (state: T) => void) => void,
(
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
) => void,
(keys?: Array<keyof T>) => void
] {
const [state, setState] = useState<T>(initialObject)
const callbackRef = useRef<(state: T) => void>()
const isFirstCallbackCall = useRef<boolean>(true)
const onChange = useCallback(
(obj: Partial<T>, callback?: (state: T) => void) => {
callbackRef.current = callback
setState((prevState) => ({ ...prevState, ...obj }))
},
[]
)
const onEventChange = useCallback(
({
target: { name, value }
}: ChangeEvent<
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
>): void => setState((prevState) => ({ ...prevState, [name]: value })),
[]
)
const arrayToObject = (keys: Array<keyof T>): T => {
if (!keys.length) return initialObject
const initial: any = {}
keys.reduce((acc, cur) => (initial[cur] = initialObject[cur]), initial)
return initial
}
const resetState = (keys?: Array<keyof T>) =>
keys
? setState((prevState) => ({ ...prevState, ...arrayToObject(keys) }))
: setState(initialObject)
useEffect(() => {
if (isFirstCallbackCall.current) {
isFirstCallbackCall.current = false
return
}
callbackRef.current?.(state)
}, [state])
return [state, onChange, onEventChange, resetState]
}
export default useObjectState
Usage
interface State {
id: string
password: string
}
const [{ id, password }, setState, onChange, resetState] =
useObjectState<State>({ id: '', password: '' })
<form>
<input value={id} name='id' onChange={onChange} />
<input value={password} name='password' type='password' onChange={onChange} />
<button type="button" onClick={() => resetState(['id'])}>Reset ID</button>
</form>