jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
34k stars 2.79k forks source link

formik file upload handling #3943

Closed KyuubiTila closed 9 months ago

KyuubiTila commented 10 months ago

Bug report

import React from 'react'; import { Formik, Form, Field, ErrorMessage } from 'formik';

const CreateProductCard = ({ addProduct, initialValues, validationSchema }) => { return ( <Formik initialValues={initialValues} onSubmit={(data, params) => { console.log(data); // addProduct(data); // params.resetForm(); }} validationSchema={validationSchema}

      <Field
        autoComplete="off"
        type="text"
        name="brand"
        placeholder="enter brand"
        className="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 dark:shadow-sm-light"
        required
      />
      <span className="text-red-500">
        <ErrorMessage name="brand" component="span" />
      </span>
    </div>
    <div className="mb-6">
      <label
        htmlFor="repeat-password"
        className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
      >
        CATEGORY
      </label>

      <Field
        autoComplete="off"
        type="text"
        name="category"
        placeholder="enter category"
        className="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 dark:shadow-sm-light"
        required
      />
      <span className="text-red-500">
        <ErrorMessage name="category" component="span" />
      </span>
    </div>
    <div className="mb-6">
      <label
        htmlFor="repeat-password"
        className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
      >
        PRICE
      </label>

      <Field
        autoComplete="off"
        type="number"
        name="price"
        placeholder="enter price"
        className="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 dark:shadow-sm-light"
        required
      />
      <span className="text-red-500">
        <ErrorMessage name="price" component="span" />
      </span>
    </div>
    <div className="mb-6">
      <label
        htmlFor="image"
        className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
      >
        IMAGE
      </label>
      <Field
        type="file"
        name="image"
        accept=".jpg, .jpeg, .png, .gif"
        className="block w-full"
      />
      <ErrorMessage name="image" component="div" className="text-red-500" />
    </div>
    <button
      type="submit"
      className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
    >
      CREATE PRODUCT
    </button>
  </Form>
</Formik>

); };

export default CreateProductCard;

Current Behavior

the image isn't returning the image field as a file object rather it returns just a string of text

Expected behavior

I ought to have a file object for the uploaded image

Reproducible example

Suggested solution(s)

Additional context

Your environment

Software Version(s)
Formik 2.4.3
React 18.2.0
TypeScript javascript
Browser chrome
npm/Yarn npm
Operating System mac os high sierra
Janelaxagh commented 10 months ago

@KyuubiTila To fix this issue and correctly handle file uploads, you can make use of the onFileChange function provided by Formik. Here's how you can modify your CreateProductCard component to handle file uploads correctly:

import React from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';

const CreateProductCard = ({ addProduct, initialValues, validationSchema }) => {
  return (
    <Formik
      initialValues={initialValues}
      onSubmit={(data, params) => {
        console.log(data);
        // addProduct(data);
        // params.resetForm();
      }}
      validationSchema={validationSchema}
    >
      {({ setFieldValue }) => (
        <Form>
          {/* ... other form fields ... */}
          <div className="mb-6">
            <label
              htmlFor="image"
              className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
            >
              IMAGE
            </label>
            <input
              type="file"
              name="image"
              accept=".jpg, .jpeg, .png, .gif"
              className="block w-full"
              onChange={(event) => {
                // Set the field value to the selected file
                setFieldValue("image", event.currentTarget.files[0]);
              }}
            />
            <ErrorMessage name="image" component="div" className="text-red-500" />
          </div>
          <button
            type="submit"
            className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
          >
            CREATE PRODUCT
          </button>
        </Form>
      )}
    </Formik>
  );
};

export default CreateProductCard;
KyuubiTila commented 9 months ago

still no way out, formik seems to not be sending the image as a file object.

KyuubiTila commented 9 months ago

yeahhh so here is the solution that works. Just figured it out

FIRST THING TO DO You can create one component like this, just a raw component.

import React from 'react';

function FileUpload(props) { const {field, form} = props;

const handleChange = (e) => { const file = e.currentTarget.files[0]; const reader = new FileReader(); const imgTag = document.getElementById("myimage"); imgTag.title = file.name; reader.onload = function(event) { imgTag.src = event.target.result; }; reader.readAsDataURL(file); form.setFieldValue(field.name, file); };

return (

handleChange(o)} className={'form-control'}/>

); }

export default FileUpload;

SECONDLY, IMPORT IN YOUR FORMIK CONTROLLED FORM

And use this like this inside the form

<Field name="image" component={FileUpload} // import from the component you just created above />

SOLUTION CREDIT : Sonukr, on Apr 14, 2019

Screen Shot 2024-02-11 at 19 08 57

KyuubiTila commented 9 months ago

Ths is what my code looks like 'use client'; import React from 'react'; import { Formik, Form, Field } from 'formik'; import FileUpload from '@/components/fileupload';

const UpdateProfile = () => { const initialValues = { username: '', bio: '', profilePhoto: '', };

const handleSubmit = (values) => { console.log('Form values:', values); };

return (

{({ isSubmitting }) => (
)}

); };

export default UpdateProfile;

and the component

import React from 'react';

function FileUpload(props) { const { field, form } = props;

const handleChange = (e) => { const file = e.currentTarget.files[0]; const reader = new FileReader(); const imgTag = document.getElementById('myimage'); imgTag.title = file.name; reader.onload = function (event) { imgTag.src = event.target.result; }; reader.readAsDataURL(file); form.setFieldValue(field.name, file); };

return (

handleChange(o)} className={'form-control'} />

); }

export default FileUpload;

AND HERE IS THE RESULT: Screen Shot 2024-02-11 at 19 13 09