'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:
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 |