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>