reactjsreact-reduxreducersformik

ReactJS: How to handle Image / File upload with Formik?


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.


Solution

  • 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} />