Search code examples
reactjstypescriptformik

How to useFormik pass Props?


using Reactjs with typescript

I want to pass the useFormik hook to the component props.

The reason for this is to reduce unnecessary lines and increase reuse.

My current code

...
const formik = useFormik({
    initialValues: { userName: ''},
    validationSchema,
    onSubmit: (values) => {}
})

return (
  <Form>
    {/* A place to make a component. */}
    <Text
       id="userName"
       fullWidth
       label="Name"
       defaultValue={formik.values.userName}
       onChange={formik.handleChange}
       onBlur={formik.handleBlur}
       error={formik.touched.userName && Boolean(formik.errors.userName)}
       helperText={formik.touched.userName && formik.errors.userName}
    >
    {/* A place to make a component. */}
  </Form>
)

Custom component, which is the main point of the question.


interface props {
    id: string;
    formik : what, // How do I deliver the prop here?
  }

const TextFieldCustom = ({ id, formik }: props) => {
    return (
     <Text
         id={id}
         fullWidth
         label={id}
         defaultValue={formik.values.userName}
         onChange={formik.handleChange}
         onBlur={formik.handleBlur}
         error={formik.touched.userName && Boolean(formik.errors.userName)}
         helperText={formik.touched.userName && formik.errors.userName}
    >

  );
};

My code completed because of your answer.

...
const formik = useFormik({
    initialValues: { userName: ''},
    validationSchema,
    onSubmit: (values) => {}
})

return (
  <Form>
    {/* A place to make a component. */}
    <TextFieldCustom id="username" formik={formik}/>
    {/* A place to make a component. */}
  </Form>
)

I want your good solution.


Solution

  • If you want to access formik helpers from the child component you can use useFormikContext. I think it's easier.

    From your code I would also recommend to use Formik component as parent of Form component as it needs it but that could be another thing.

    Here's what I would do (note that I've substituted Form component in favor of form html tag):

    Parent component:

    export interface IFormData {
      username: string;
    }
    
    const validationSchema = Yup.object().shape({
      userName: Yup.string()
        .required("Username required"),
    });
    
    const ParentComponent = () => {
      const formikConfig = useFormik({
        initialValues: { userName: "" },
        validationSchema,
        onSubmit: (values) => {},
      });
    
      return (
        <form onSubmit={formikConfig.handleSubmit}>
          <TextCompo id="id" />
        </form>
      );
    };
    
    export default ParentComponent;
    

    Children component:

    interface ITextCompoProps {
      id: string;
    }
    
    const TextFieldCustom = (props: ITextCompoProps) => {
      const { id } = props;
      const context = useFormikContext<IFormData>();
    
      return (
        <Text
          id={id}
          fullWidth
          label={id}
          defaultValue={context.values.userName}
          onChange={context.handleChange}
          onBlur={context.handleBlur}
          error={!!(context.errors.username && context.touched.username)}
          helperText={context.touched.username && context.errors.username}
        />
      );
    };
    
    export default TextFieldCustom;