bmvantunes / youtube-2020-june-multi-step-form-formik

A repository with a multi-step form using Formik, Yup and Material-UI
https://youtu.be/l3NEC4McW3g
MIT License
100 stars 68 forks source link

Passing formik props to children #2

Closed kieranbop closed 4 years ago

kieranbop commented 4 years ago

Absolutely brilliant demo and video.

I've been trying to use react-bootstrap instead of formiks fields, but what I want to do is pass additional formik props in the children so I can use formiks (values, errors and touched) implementation.

Do you perhaps know how I could pass down formik props to the FormikStep so that any react bootstrap field I implement can access formiks touched, values and errors for example?

https://react-bootstrap.github.io/components/forms/#forms-validation-libraries <-- example here of the extra formik items mentioned above.

Thanks

bmvantunes commented 4 years ago

Thank you very very much =D

What you want to do is called "render props" - you can find more about render props over here: https://reactjs.org/docs/render-props.html

Usually, when I have to use react-bootstrap I prefer to create my own wrappers - that way I don't have to repeat all the properties multiple times across my application. All the error messages, formatting etc, is handled in my wrapper component. Inside my form I just do something like:

<MyBootstrapInput name="name" controlId="some-id-to-my-label" label="Your name" /> 

And "magically" all validation happens without having to repeat logic anywhere else in my app. If I want to change how that component shows validation errors, I only change it in one place, and all my application reflects that change, which is great.

If you want to use something like that you can start with a simple wrapper like this and evolve to your needs:

import { ErrorMessage, useField } from 'formik';
import React from 'react';
import { Form, FormControlProps } from 'react-bootstrap';

export interface MyBootstrapInputProps extends FormControlProps {
  controlId?: string;
  label?: string;
  name: string;
  // you can add more props if need them =)
}

export function MyBootstrapInput({
  label,
  controlId,
  name,
  ...props
}: MyBootstrapInputProps) {
  const [field, meta] = useField(name);

  return (
    <Form.Group controlId={controlId}>
      <Form.Label>{label}</Form.Label>

      <Form.Control
        isValid={meta.touched && !meta.error}
        isInvalid={meta.touched && !!meta.error}
        {...props}
        {...field}
        name={name}
      />

      <ErrorMessage name={name}>
        {msg => (
          <Form.Control.Feedback type="invalid">{msg}</Form.Control.Feedback>
        )}
      </ErrorMessage>
    </Form.Group>
  );
}

Let me know if this is helpful =)

kieranbop commented 4 years ago

Oh wow this worked like a dream. Thankyou so much for the guidance. Much appreciated.

kieranbop commented 4 years ago

I tried swapping out react bootstrap form for the formik form, but this resulted in the transitioning to stop working. It seems though if you use that component above and replace the formik fields, once we progress on any button click it seems to be triggering feedback across everything. Any ideas on how to stop that? I can throw up a sandbox if required? Thanks

bmvantunes commented 4 years ago

You shouldn't remove the "Form" component from formik (that component is handling the form onsubmit).

Any reason for swapping that component by the react-bootstrap one? If yes, feel free to create a codesandbox and I'll try to have a look tonight or tomorrow night =)