youtube-2020-june-multi-step-form-formik
youtube-2020-june-multi-step-form-formik copied to clipboard
How to use FormikStep inside a component within a FormikStepper
Hi,
Thanks for the wonderful video and for providing all the code.
I am creating a site and would like to make FormikSteps within component so I can reuse them and make sure I keep the code DRY.
The problem I am having is when calling the component which returns a FormikStep inside a FormikStepper, the label and validationSchema don't exist on the parent, therefore the FormikStepper cannot access them.
Please see some example code:
MyForm.js
export default function MyForm() {
const classes = useStyles();
return (
<Box p={2}>
<FormikStepper
initialValues={{ name: "" }}
onSubmit={(values, actions) => {
actions.setSubmitting(false);
console.log(values);
}}
>
<ReusableStep />
</FormikStepper>
</Box>
);
}
ReusableStep.js
export default function ReusableStep() {
return (
<FormikStep
label="Reusable Form Step"
validationSchema={Yup.object({})}
>
<Grid item xs={12} sm={true}>
<Field
component={TextField}
type="text"
name="name"
label="Name"
fullWidth
/>
</Grid>
<FormikStep />
)
}
When in the current state, the validation doesn't work, and the Material UI Step doesn't get a label.
The only way I can get it to work is by passing validationSchema and label to the ReusableStep:
<ReusableStep
label="Reusable Form Step"
validationSchema={Yup.object({})}
/>
This is not great, as I would have to repeat defining the schema each time I want to use the FormikStep.
Please could you help me get this sorted?
Many thanks, Zac
I'd just like to add that ReusableStep will not be reused in the same form. It will be reused on another form on another page.
That's why the label is hard coded within it, and not passed as a prop into it.
Thanks
Hi Thank you for this.And I want to add Autocomplete in this project.But I can't .Please help me. `
export default function Threenumber() { const [addtocart] = useAddToCartMutation(); const [title, setTitle] = useState([]); const { data } = useTakeThreeDateQuery(); const [cityId, setCityId] = useState({ city_id: "" });
return ( <GridItem xs={12} sm={12} md={12}> <Card style={{ marginTop: "-15px" }}> <CardContent> <Box style={{ display: "flex", flexDirection: "column", alignItems: "center", }} > <Chip style={{ color: "#42ba96", marginTop: "10px" }} variant="outlined" avatar={ <Avatar style={{ backgroundColor: "#8b17d1", color: "#FFFFFF" }} > 3D </Avatar> } label="Test Label" /> </Box> <FormikStepper initialValues={{ firstName: "", lastName: "", millionaire: false, money: 0, description: "", }} onSubmit={async (values) => { await sleep(3000); console.log("values", values); console.log("states", cityId.city_id); }} > <FormikStep label="Step One"> <Box paddingBottom={2}> <Autocomplete id="firstName" // ref={ref0} // name="firstName" options={positionItems} getOptionLabel={(option) => String(option.value)} fullWidth style={{ marginTop: "25px" }}
renderInput={(params) => (
<Field
{...params}
id="firstName"
label="Choose 3D Number"
name="firstName"
variant="outlined"
component={TextField}
required={true}
/>
)}
/>
</Box>
<Box paddingBottom={2}>
<Field
fullWidth
name="lastName"
component={TextField}
label="Last Name"
/>
</Box>
<Box paddingBottom={2}>
<Field
name="millionaire"
type="checkbox"
// component={CheckboxWithLabel}
Label={{ label: "I am a millionaire" }}
/>
</Box>
</FormikStep>
<FormikStep
label="Step Two"
validationSchema={object({
money: mixed().when("millionaire", {
is: true,
then: number()
.required()
.min(
1_000_000,
"Because you said you are a millionaire you need to have 1 million"
),
otherwise: number().required(),
}),
})}
>
<Box paddingBottom={2}>
<Field
fullWidth
name="money"
type="number"
component={TextField}
label="All the money I have"
/>
</Box>
</FormikStep>
<FormikStep label="Confirm">
<Box paddingBottom={2}>
<Field
fullWidth
name="description"
component={TextField}
label="Description"
/>
</Box>
</FormikStep>
</FormikStepper>
</CardContent>
</Card>
</GridItem>
); }
export interface FormikStepProps extends Pick<FormikConfig<FormikValues>, "children" | "validationSchema"> { label: string; }
export function FormikStep({ children }: FormikStepProps) { return <>{children}</>; }
export function FormikStepper({ children, ...props }: FormikConfig<FormikValues>) { const childrenArray = React.Children.toArray( children ) as React.ReactElement<FormikStepProps>[]; const [step, setStep] = useState(0); const currentChild = childrenArray[step]; const [completed, setCompleted] = useState(false);
function isLastStep() { return step === childrenArray.length - 1; }
return ( <Formik {...props} validationSchema={currentChild.props.validationSchema} onSubmit={async (values, helpers) => { if (isLastStep()) { await props.onSubmit(values, helpers); setCompleted(true); } else { setStep((s) => s + 1); helpers.setTouched({}); } }} > {({ isSubmitting}) => ( <Form> <Stepper alternativeLabel activeStep={step}> {childrenArray.map((child, index) => ( <Step key={child.props.label} completed={step > index || completed} > <StepLabel>{child.props.label}</StepLabel> </Step> ))} </Stepper>
{currentChild}
<Grid container spacing={2}>
{step > 0 ? (
<Grid item>
<Button
disabled={isSubmitting}
variant="contained"
color="primary"
onClick={() => setStep((s) => s - 1)}
>
Back
</Button>
</Grid>
) : null}
<Grid item>
<Button
startIcon={
isSubmitting ? <CircularProgress size="1rem" /> : null
}
disabled={isSubmitting}
variant="contained"
color="primary"
type="submit"
>
{isSubmitting ? "Submitting" : isLastStep() ? "Submit" : "Next"}
</Button>
</Grid>
</Grid>
</Form>
)}
</Formik>
); } `