'How could we pass FileReader's result to parent's state in React?

Hello and thank you for your time.

I am currently learning React.

I am trying to refactor a component to separate and put the state into a top level component, called App, and make the ImageUpload, which renders a file input and a canvas, a stateless component.

The code:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import registerServiceWorker from './registerServiceWorker';

class ImageUpload extends React.Component {


    _handleImageChange(e) {
        console.log("Enter");
        e.preventDefault();

        let reader = new FileReader();
        let newFile = e.target.files[0];
        console.log("File event: " + newFile);
        this.props.onChange(newFile, reader);
        console.log("File event after: " + newFile);

        reader.readAsDataURL(newFile)
    }

    render() {
        let imagePreviewUrl = this.props.upload;
        console.log("URL IS: " + imagePreviewUrl);
        let $imagePreview = null;
        if (imagePreviewUrl) {
            $imagePreview = (<img src={imagePreviewUrl}/>);
        } else {
            $imagePreview = (
                <div className="previewText">Por favor seleccione una imagen de su ordenador para visualizarla</div>);
        }

        return (
            <div className="previewComponent">
                <form>
                    <input className="fileInput"
                           type="file"
                           onChange={(e) => this._handleImageChange(e)}/>
                </form>
                <div className="imgPreview">
                    {$imagePreview}
                </div>
            </div>
        )
    }
}

class App extends React.Component {
    state = {file: '', upload: ''};

    setFileFromUrl = (newFile, reader) => {
        console.log("The image file in APP method is: " + newFile);
        console.log("reader is:  " + reader);
        this.setState({
            file: newFile,
            upload: reader.result
        })
        ;
        console.log("Changed image file " + (this.state.file));
        console.log("Changed READER TO " + this.state.upload);
    };

    render() {
        return (
            <div>
                <ImageUpload onChange={this.setFileFromUrl} file={this.state.file} upload={this.state.upload}/>
            </div>
        )
    }
}

ReactDOM.render(<App/>, document.getElementById("mainApp"));
registerServiceWorker();

HTML:

<h3>Suba una imagen desde el ordenador, para mostrarla:</h3>
<div id="mainApp"></div>

The code is based on: https://codepen.io/hartzis/pen/VvNGZP

I have studied the official tutorial and this course: https://app.pluralsight.com/library/courses/react-js-getting-started/table-of-contents

The difficult I am facing is that I would like to be able to update App's state from ImageUpload child component. Currently I am able to update App's state's file. However, I do not know how to do the same with App's state's upload condition, which tells canvas whether to show a string asking for inputting and image or to show it.

Output: First when we load the page the state is:

State
file:
""
upload:
""

Then when we upload the image:

State
file:
File{…}
upload:
null

Why is upload null?

Also log result is:

index.js:10 Enter
index.js:15 File event: [object File]
index.js:52 The image file in APP method is: [object File]
index.js:53 reader is:  [object FileReader]
index.js:59 Changed image file 
index.js:60 Changed READER TO 
index.js:17 File event after: [object File]
index.js:24 URL IS: null

So we see that File flows from ImageUpload to App's state through setFileFromUrl method, when an onChange is present in the ImageUpload.

Why is URL null?

We know that FileReader is present in setFileFromUrl, and it is supposed that we update App's state's upload in:

upload: reader.result

Also, with the Development Tools, I have tried to manually change it to true:

And the output in the canvas is that it tries to show something, but it only shows a default thumbnail: enter image description here

Could you help me please, thank you!!! 😁



Solution 1:[1]

You forgot to use reader.onloadend, add it to wrap your onChange call :

_handleImageChange(e) {
    console.log("Enter");
    e.preventDefault();
    let reader = new FileReader();
    let newFile = e.target.files[0];
    reader.onloadend = () => {
        this.props.onChange(newFile, reader);
    }
    reader.readAsDataURL(newFile)
}

Working codepen : https://codepen.io/Dyo/pen/LQxEYQ?editors=0110

Also, don't log this.state just after setState it won't log updated values because setState is async.

Solution 2:[2]

With the react function component, one could also use the below solution

import React,{useState} from 'react';
import {Buffer} from 'buffer';


function FileUpload() {

  const [filedata,setFiledata] = useState({fileBuffer:'No file uploaded'});

  const uploadFile = async (event) =>{
    console.log(filedata);
    event.preventDefault();
    
    //getting file from user
    const fileName = event.target.files[0];
    console.log('You uploaded => ',fileName.name);
    const fileReader = new window.FileReader();
    //reading filecontent as buffer
    fileReader.readAsArrayBuffer(fileName);
    console.log(fileReader);
    
    fileReader.onloadend = () =>{
      console.log('fileRead check in progress...');
      // console.log(Buffer(fileReader.result));
      setFiledata({fileBuffer: Buffer(fileReader.result)});
      console.log('buffer is',Buffer(fileReader.result));
    }
    
  }
  
  const fileSubmit = (event) =>{
    event.preventDefault();
    console.log('File submitted');
    console.log('filedata is',filedata.fileBuffer);
  }
  
  return (
    <div className="FileUpload">
    
      <form onSubmit={fileSubmit}>
        <input type="file" onChange={uploadFile}></input> {/*onChange */}
        {/* {filedata.fileBuffer} */}
        <input type='submit' ></input> {/*onSubmit */}
      </form>
        
        
    
    </div>
  );
}

export default FileUpload;

below solution using native Node.js modules

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dyo
Solution 2 Jordan TheDodger