import { useCallback, useReducer } from 'react' export type AnswerValue = string export type QuestionState = { selectedValue?: string | string[] textInputValue: string } type State = { currentQuestionIndex: number answers: Record questionStates: Record isInTextInput: boolean } type Action = | { type: 'next-question' } | { type: 'prev-question' } | { type: 'update-question-state' questionText: string updates: Partial isMultiSelect: boolean } | { type: 'set-answer' questionText: string answer: string shouldAdvance: boolean } | { type: 'set-text-input-mode'; isInInput: boolean } function reducer(state: State, action: Action): State { switch (action.type) { case 'next-question': return { ...state, currentQuestionIndex: state.currentQuestionIndex + 1, isInTextInput: false, } case 'prev-question': return { ...state, currentQuestionIndex: Math.max(0, state.currentQuestionIndex - 1), isInTextInput: false, } case 'update-question-state': { const existing = state.questionStates[action.questionText] const newState: QuestionState = { selectedValue: action.updates.selectedValue ?? existing?.selectedValue ?? (action.isMultiSelect ? [] : undefined), textInputValue: action.updates.textInputValue ?? existing?.textInputValue ?? '', } return { ...state, questionStates: { ...state.questionStates, [action.questionText]: newState, }, } } case 'set-answer': { const newState = { ...state, answers: { ...state.answers, [action.questionText]: action.answer, }, } if (action.shouldAdvance) { return { ...newState, currentQuestionIndex: newState.currentQuestionIndex + 1, isInTextInput: false, } } return newState } case 'set-text-input-mode': return { ...state, isInTextInput: action.isInInput, } } } const INITIAL_STATE: State = { currentQuestionIndex: 0, answers: {}, questionStates: {}, isInTextInput: false, } export type MultipleChoiceState = { currentQuestionIndex: number answers: Record questionStates: Record isInTextInput: boolean nextQuestion: () => void prevQuestion: () => void updateQuestionState: ( questionText: string, updates: Partial, isMultiSelect: boolean, ) => void setAnswer: ( questionText: string, answer: string, shouldAdvance?: boolean, ) => void setTextInputMode: (isInInput: boolean) => void } export function useMultipleChoiceState(): MultipleChoiceState { const [state, dispatch] = useReducer(reducer, INITIAL_STATE) const nextQuestion = useCallback(() => { dispatch({ type: 'next-question' }) }, []) const prevQuestion = useCallback(() => { dispatch({ type: 'prev-question' }) }, []) const updateQuestionState = useCallback( ( questionText: string, updates: Partial, isMultiSelect: boolean, ) => { dispatch({ type: 'update-question-state', questionText, updates, isMultiSelect, }) }, [], ) const setAnswer = useCallback( (questionText: string, answer: string, shouldAdvance: boolean = true) => { dispatch({ type: 'set-answer', questionText, answer, shouldAdvance, }) }, [], ) const setTextInputMode = useCallback((isInInput: boolean) => { dispatch({ type: 'set-text-input-mode', isInInput }) }, []) return { currentQuestionIndex: state.currentQuestionIndex, answers: state.answers, questionStates: state.questionStates, isInTextInput: state.isInTextInput, nextQuestion, prevQuestion, updateQuestionState, setAnswer, setTextInputMode, } }