27

I am designing a profile page for my site using ReactJS. Now my question is how do I upload the image from local machine and save it to the database and also displaying it in the profile page

import React, {Component} from 'react';
import { connect } from 'react-redux';
import { AccountAction } from '../../actions/user/AccountPg1Action';
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';

class AccountInfo extends Component {
  constructor(props) {
    super(props) 
    this.state = {
      currentStep: 1,
      userAccountData: {
        userid: '',
        useravtar: '',
        attachement_id: '',
   }
  }
 }

handleFileUpload = (event) => {
  this.setState({useravtar: event.currentTarget.files[0]})
};

handleChange = event => {
    const {name, value} = event.target
    this.setState({
      [name]: value
    })    
  }

handleSubmit = event => {
    let that = this;
    const { AccountAction } = that.props;
    event.preventDefault();

    let accountInputs = {
      userid: 49,
      useravtar: that.state.image,
      attachement_id: 478,
}
    that.setState({
      userAccountData: accountInputs,
    })

    AccountAction(accountInputs)
  }
AccountInfoView = () => {
console.log(this.state.useravtar)
    return (
      <section id="account_sec" className="second_form">
      <div className="container">
      <React.Fragment>
        <Formik
          initialValues={‌{
            file: null,
            email: '',
            phone: ''
          }}
          validationSchema={accountInfoSchema}
          render={(values) => {
          return(
        <Form onSubmit={this.handleSubmit}>
        <Step1 
          currentStep={this.state.currentStep} 
          handleChange={this.handleChange}
          file= {this.state.useravtar}
          handleFileUpload={this.handleFileUpload}
          />
          </Form>
        );
      }}
      />
      </React.Fragment>
      )
  }

  render() {    

    return (
      <div>{this.authView()}</div>
    )
  }
}

function Step1(props) {
console.log(props.useravtar)
  if (props.currentStep !== 1) {
    return null
  } 

  return(
    <div className="upload">
        <label htmlFor="profile">
          <div className="imgbox">
            <img src="images/trans_116X116.png" alt="" />
            <img src={props.useravtar} className="absoImg" alt="" />
          </div>
        </label>
<input id="file" name="file" type="file" accept="image/*" onChange={props.handleFileUpload}/>
        <span className="guide_leb">Add your avatar</span>
      </div>
  )
}

When I do console in handleChange action for event.target.file[0] it responds with undefined.

Also, doing a console.log(this.state.useravtar) in handleSubmit action it shows a pathname like c:/fakepath/imgname.jpg

P.S: I have a multiple forms so I am using it in a Step wise. And i am using Redux Reducer for storing the data.

I have referred this link but my requirement is not looking like this.

Alex
  • 826
  • 1
  • 10
  • 24
TMA
  • 1,004
  • 1
  • 15
  • 31
  • 1
    have you tried using a form data object - https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects – Amr May 15 '19 at 13:12
  • @SumanthMadishetty : i have used a Formik library so i have used a `Field` component from this library. https://jaredpalmer.com/formik/ – TMA May 16 '19 at 04:31
  • @Taalavya formik doesnot have file upload component, you have to use html input and use ` setFieldValue` method of formik to set the data – Sumanth Madishetty May 16 '19 at 04:39

1 Answers1

33

Formik doesnot support fileupload by default, But you can try the following

<input id="file" name="file" type="file" onChange={(event) => {
  setFieldValue("file", event.currentTarget.files[0]);
}} />

Here "file" represents the key that you are using for holding the file

And on submit you can get the filename, size etc for the file by using

onSubmit={(values) => {
        console.log({ 
              fileName: values.file.name, 
              type: values.file.type,
              size: `${values.file.size} bytes`
            })

If you want to set the file into components state then you can use

onChange={(event) => {
  this.setState({"file": event.currentTarget.files[0]})};
}}

According to your code, you have to handle file upload as below

In AccountInfo add a function to handle file upload

handleFileUpload = (event) => {
this.setState({WAHTEVETKEYYOUNEED: event.currentTarget.files[0]})};
}

And pass the same function to Step1 Component as below

    <Step1 
      currentStep={this.state.currentStep} 
      handleChange={this.handleChange}
      file= {this.state.image}
      handleFileUpload={this.handleFileUpload}
      />

In Step1 Component where you upload the file, Change the input as

<input id="file" name="file" type="file" accept="image/*" onChange={props.handleFileUpload}/>

If you need to preview the uploaded image then you can create a blob and pass the same as source for image as below

<img src={URL.createObjectURL(FILE_OBJECT)} /> 

EDIT-1

As URL.createObjectURL method is deprecated due to security issues, we need to use srcObject for Media Elements, to use that you can use ref to assign srcObject, for example

Assuming you are using class Components,

Constructor

in constructor you can use

constructor(props) {
  super(props)
  this.imageElRef = React.createRef(null)
}

HANDLE CHANGE FUNCTION

handleFileUpload = (event) => {
  let reader = new FileReader();
let file = event.target.files[0];
reader.onloadend = () => {
  this.setState({
    file: reader.result
  });
};
reader.readAsDataURL(file);
}

Element

<img src={this.state.file} /> 
Sumanth Madishetty
  • 2,054
  • 1
  • 7
  • 16
  • 1
    Where to define this `setFieldValue`, it throws me an error of undefined like: `./src/components/user/AccountInfo.jsx Line 266: 'setFieldValue' is not defined no-undef` – TMA May 16 '19 at 05:10
  • 4
    `setFieldValue` is obtained from ``, refer: https://jaredpalmer.com/formik/docs/api/formik#setfieldvalue-field-string-value-any-shouldvalidate-boolean-void – Dani Vijay May 16 '19 at 05:30
  • @Taalavya if you want to store the file directly to state you can use the method that i mentioned – Sumanth Madishetty May 16 '19 at 05:35
  • @SumanthMadishetty yess correct but it throws this error from a `function Step1(props){...}` function. https://prnt.sc/np7gnp – TMA May 16 '19 at 06:00
  • What do u mean by using props.setState...can you update the code with implementation – Sumanth Madishetty May 16 '19 at 06:10
  • @Taalavya Updated my code,According to your implementation, Please check – Sumanth Madishetty May 16 '19 at 06:24
  • yess now i am getting some result. So, doing `console.log(this.state.useravtar)` i am getting this result. https://prnt.sc/np84z8 But how can i get a path of uploaded image in for preview ? I did something like this : `` `` so this `props.useravtar` gives an undefined value for preview image. `handleFileUpload = (event) => { this.setState({useravtar: event.currentTarget.files[0]}) };` – TMA May 16 '19 at 07:05
  • @SumanthMadishetty: can you please update your answer that will be help to other developers also in future. – TMA May 16 '19 at 08:20
  • Interesting. I'm getting an error when I used `setFieldValue()` the way you suggest, @muthanth. This is the error: `Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.` – codeinaire Nov 18 '19 at 01:06
  • **createObjectURL** is now deprecated in most of the browsers. I researched it and found that instead of src, we need to use **srcObject**. But I don't know how to use in tag. Anyone out there and @SumanthMadishetty Please look into it. It will very helpful for me a lot. – Utkarsh May 26 '20 at 13:00
  • Thanks, @SumanthMadishetty for the edit. I'm facing an issue which is when I upload the image, my modal refreshes and the file input field lost the value. – Utkarsh May 27 '20 at 17:09
  • Is there a useFormik example? because I don't think useFormik has a setFieldValue method? – alexr89 Aug 02 '20 at 16:38
  • I get `InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable` in Firefox. Need to set the input's value to an empty string (based on the error message I got in Chrome) to avoid the error. – Dorklord Oct 30 '20 at 08:31
  • facing this error ==> Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string. – Hassan Ali Shahzad Mar 14 '21 at 08:54