Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use FormikStep inside a component within a FormikStepper #10

Open
ossiozac opened this issue Feb 19, 2021 · 2 comments
Open

How to use FormikStep inside a component within a FormikStepper #10

ossiozac opened this issue Feb 19, 2021 · 2 comments

Comments

@ossiozac
Copy link

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

@ossiozac
Copy link
Author

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

@khrukawa
Copy link

khrukawa commented Oct 28, 2022

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 (

<Card style={{ marginTop: "-15px" }}>

<Box
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<Chip
style={{ color: "#42ba96", marginTop: "10px" }}
variant="outlined"
avatar={
<Avatar
style={{ backgroundColor: "#8b17d1", color: "#FFFFFF" }}
>
3D

}
label="Test Label"
/>

<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);
}}
>


<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, "children" | "validationSchema"> {
label: string;
}

export function FormikStep({ children }: FormikStepProps) {
return <>{children}</>;
}

export function FormikStepper({
children,
...props
}: FormikConfig) {
const childrenArray = React.Children.toArray(
children
) as React.ReactElement[];
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}) => (



{childrenArray.map((child, index) => (
index || completed}
>
{child.props.label}

))}

      {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>

);
}
`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants