Skip to content

Instantly share code, notes, and snippets.

@sr-shifu
Last active March 19, 2024 07:49
State machine wizard states
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