import { createContext, useContext, useEffect, useState, type ReactNode } from 'react' import { getCodes, type CodeMap } from '../api/client' /** * 화면 선택지를 서버에서 한 번 받아 앱 전체에 나눠준다. * * 화면마다 따로 부르지 않는 이유는 코드가 자주 안 바뀌고, 선택지를 기다리느라 폼이 * 깜빡이는 것을 피하기 위해서다. 로그인 화면에서는 필요 없으므로 인증 이후에 감싼다. */ const CodeContext = createContext(null) type LoadState = 'loading' | 'ready' | 'error' /** * @param codes 넘기면 서버를 부르지 않고 그 값을 그대로 쓴다. 테스트에서 화면 하나만 * 떼어 그릴 때 쓰라고 열어둔 자리다. */ export function CodeProvider({ children, codes: given }: { children: ReactNode; codes?: CodeMap }) { const [codes, setCodes] = useState(given ?? null) const [state, setState] = useState(given ? 'ready' : 'loading') const [message, setMessage] = useState('') async function load() { setState('loading') try { setCodes(await getCodes()) setState('ready') } catch (e) { setMessage((e as Error).message) setState('error') } } useEffect(() => { if (!given) { void load() } }, [given]) if (state === 'loading') { return
불러오는 중…
} // 선택지를 못 받은 채로 화면을 그리면 빈 드롭다운이 뜨고, 거기서 저장하면 값이 // 지워진다. 그리지 않고 오류를 보여주는 편이 안전하다. if (state === 'error' || !codes) { return (

선택지 정보를 불러오지 못했습니다. {message}

) } return {children} } /** Provider 밖에서 부르면 개발 중에 바로 알아채도록 예외를 던진다. */ export function useCodeMap(): CodeMap { const codes = useContext(CodeContext) if (!codes) { throw new Error('CodeProvider 안에서만 쓸 수 있습니다.') } return codes }