/StateMachineWizard.tsx Secret
Last active
March 19, 2024 07:49
State machine wizard states
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const getStepView = <T, V extends string>( | |
stateMachineConfig: StateMachineConfig<T, V>, | |
stepName: V | |
): React.ComponentType => stateMachineConfig.views[stepName]; | |
function findStepPositionByName<V extends string>( | |
navHistory: NavHistoryItem<V>[], | |
stepName: V | |
) { | |
return navHistory.findIndex( | |
({ stepName: navStepName }) => navStepName === stepName | |
); | |
} | |
function aggregateWizardDataForStep<T extends object, V extends string>( | |
stepPosition: number, | |
navHistory: NavHistoryItem<V>[], | |
wizardStateBySteps: Record<V, T> | |
) { | |
if (stepPosition < 0 || stepPosition >= navHistory.length) return {}; | |
return navHistory.slice(0, stepPosition + 1).reduce( | |
(aggregated, { stepName }) => ({ | |
...aggregated, | |
...wizardStateBySteps[stepName], | |
}), | |
{} | |
); | |
} | |
export const StateMachineWizard = <T, V extends string>({ | |
// ...other props | |
stateMachineConfig, | |
initialWizardState = {}, | |
}: StateMachineWizardProps<T, V>) => { | |
if (!stateMachineConfig) { | |
throw new Error("State machine configuration is not defined!"); | |
} | |
// extract initial step name | |
const initialStepName = ( | |
typeof stateMachineConfig.initialStep === "function" | |
? stateMachineConfig.initialStep(initialWizardState) | |
: stateMachineConfig.initialStep | |
) as V; | |
// extract initial view to render | |
const InitialView = getStepView(stateMachineConfig, initialStepName); | |
// define nav history that will store history of transitionings b/w steps | |
const [navHistory, updateNavHistory] = useState<NavHistoryItem<V>[]>([ | |
{ | |
Component: <InitialView />, | |
stepName: initialStepName, | |
}, | |
]); | |
// store current step name | |
const [curStepName, setCurStepName] = useState<V>(initialStepName); | |
// store wizard state chunks for each step separately | |
// so we could build aggregated state based on what step we are on | |
const [wizardDataByStep, setWizardData] = useState< | |
Record<string, Partial<T>> | |
>({ | |
[initialStepName]: initialWizardState, | |
}); | |
const curStepPos = findStepPositionByName(navHistory, curStepName); | |
const aggregatedStepState = useMemo( | |
() => aggregateWizardDataForStep(curStepPos, navHistory, wizardDataByStep), | |
[curStepPos, navHistory, wizardDataByStep] | |
); | |
const [errors, setErrors] = useState([]); | |
const isFinalStep = stateMachineConfig.steps[curStepName].isTerminal; | |
// ...code continues | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment