Get image preview before uploading in React
JavascriptJqueryReactjsJavascript Problem Overview
Many examples of this on here but can't seem to find any for react. I have managed to convert the vanilla js to react but getting an error.
The answer looks simple enough so here I go in react:
getInitialState: function(){
return{file: []}
},
_onChange: function(){
// Assuming only image
var file = this.refs.file.files[0];
var reader = new FileReader();
var url = reader.readAsDataURL(file);
console.log(url) // Would see a path?
// TODO: concat files for setState
},
render: function(){
return(
<div>
<form>
<input
ref="file"
type="file"
name="user[image]"
multiple="true"
onChange={this._onChange}/>
</form>
{/* Only show first image, for now. */}
<img src={this.state.file[0} />
</div>
)
};
Basically all answers I have seen show something like what I have. Any difference in React app?
Regarding answer:
Javascript Solutions
Solution 1 - Javascript
Extending on Cels' answer, and avoiding memory leaks with createObjectURL
as @El Anonimo warns about, we could use useEffect
to create the preview and clean up after the component unmounts like so
useEffect(() => {
// create the preview
const objectUrl = URL.createObjectURL(selectedFile)
setPreview(objectUrl)
// free memory when ever this component is unmounted
return () => URL.revokeObjectURL(objectUrl)
}, [selectedFile])
The full code could look something like this
export const ImageUpload = () => {
const [selectedFile, setSelectedFile] = useState()
const [preview, setPreview] = useState()
// create a preview as a side effect, whenever selected file is changed
useEffect(() => {
if (!selectedFile) {
setPreview(undefined)
return
}
const objectUrl = URL.createObjectURL(selectedFile)
setPreview(objectUrl)
// free memory when ever this component is unmounted
return () => URL.revokeObjectURL(objectUrl)
}, [selectedFile])
const onSelectFile = e => {
if (!e.target.files || e.target.files.length === 0) {
setSelectedFile(undefined)
return
}
// I've kept this example simple by using the first image instead of multiple
setSelectedFile(e.target.files[0])
}
return (
<div>
<input type='file' onChange={onSelectFile} />
{selectedFile && <img src={preview} /> }
</div>
)
}
Solution 2 - Javascript
No difference, just read your image when the load
event finishes. After the load end
event handler just set your state:
getInitialState: function(){
return{file: []}
}
_onChange: function(){
// Assuming only image
var file = this.refs.file.files[0];
var reader = new FileReader();
var url = reader.readAsDataURL(file);
reader.onloadend = function (e) {
this.setState({
imgSrc: [reader.result];
})
}.bind(this);
console.log(url) // Would see a path?
// TODO: concat files
},
render: function(){
return(
<div>
<form>
<input
ref="file"
type="file"
name="user[image]"
multiple="true"
onChange={this_onChange}/>
</form>
{/* Only show first image, for now. */}
<img src={this.state.imgSrc} />
</div>
)
}
Solution 3 - Javascript
Please this worked perfectly for me
state = {
img: logo
}
handleChangeImage = e => {
this.setState({[e.target.name]: URL.createObjectURL(e.target.files[0])})
}
<input type="file" id="img" name="img" accept="image/*" className="w-100" onChange={this.handleChangeImage}/>
<img src={this.state.img} alt="img"/>
> Check the link for more details https://medium.com/@650egor/react-30-day-challenge-day-2-image-upload-preview-2d534f8eaaa
Solution 4 - Javascript
Here is a simple and working solution which displays all selected images
getInitialState: function(){
return{file: []}
}
_onChange: (event)=>{
this.setState({
imgs: event.target.files
})
},
render: function(){
return(
<div>
<form>
<input
ref="file"
type="file"
name="user[image]"
multiple="true"
onChange={this._onChange}/>
</form>
{/* Display all selected images. */}
{this.state.imgs && [...this.state.imgs].map((file)=>(
<img src={URL.createObjectURL(file)} />
))}
</div>
)
}
Solution 5 - Javascript
Try This multiple Upload and Preview:
import React,{useState} from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
import PhotoCamera from '@material-ui/icons/PhotoCamera';
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
},
},
input: {
display: 'none',
},
}));
export default function UploadButtons() {
const classes = useStyles();
const [files,setFile]=useState([]);
const handlerFile=(e)=>{
console.log(e.target.files);
let allfiles=[]
for(let i=0;i<e.target.files.length;i++){
allfiles.push(e.target.files[i]);
}
if(allfiles.length>0){
setFile(allfiles);
}
};
return (
<div className={classes.root}>
{files.map((file, key) => {
return (
<div key={key} className="Row">
<span className="Filename">
{file.name}
<img src={URL.createObjectURL(file)} alt={file.name} />
</span>
</div>
)
})}
<input
accept="image/*"
className={classes.input}
id="contained-button-file"
multiple
type="file"
onChange={handlerFile}
/>
<label htmlFor="contained-button-file">
<Button variant="contained" color="primary" component="span">
Upload
</Button>
</label>
);
}
Solution 6 - Javascript
There is my solution. Very simple and easy to use.
JSX
<form onSubmit={this.onSubmit} >
<input
ref="uploadImg"
type="file"
name="selectedFile"
onChange={this._onChange}
/>
</form>
<img src={this.state.imageUrl} />
And function
_onChange = (e) => {
const file = this.refs.uploadImg.files[0]
const reader = new FileReader();
reader.onloadend = () => {
this.setState({
imageUrl: reader.result
})
}
if (file) {
reader.readAsDataURL(file);
this.setState({
imageUrl :reader.result
})
}
else {
this.setState({
imageUrl: ""
})
}
}
And of course You need to have state like imageUrl
which is our src in jsx.
Solution 7 - Javascript
I had a crazy time trying to get this to work with multiple files, so if anyone else is having this problem, here you go:
const onFileChange = e => {
e.preventDefault();
const filesList = e.target.files;
if (filesList.length === 0) return;
//Spread array to current state preview URLs
let arr = [...data.imagePreviewUrls];
for (let i = 0; i < filesList.length; i++) {
const file = filesList[i];
const reader = new FileReader();
reader.onload = upload => {
//push new image to the end of arr
arr.push(upload.target.result);
//Set state to arr
setData({
...data,
imagePreviewUrls: arr
});
};
reader.readAsDataURL(file);
}
};
Essentially the for loop resolves before even the first onload event fires, resetting the state every time it iterates. To resolve this just make an array outside of the for loop and push the new item to it onload. This will handle multiple files even if the user closes the file dialog and later chooses to upload more, as long as state hasn't been reset.
Solution 8 - Javascript
You can use base64 and use that result in the img src. Here I am using typescript, but you can use the same logic with pure JS
(file: Blob):Promise< string | ArrayBuffer | null> => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
}),
Solution 9 - Javascript
This is based on @Mohammed's answer with an example of how to use it
const loadImage = (file: Blob) =>
new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () =>
typeof reader.result === "string" ? resolve(reader.result) : reject(reader.result);
reader.onerror = error => reject(error);
});
export const FilePreview: React.FC<{ file: File }> = ({ file }) => {
const [imageUrl, setImageUrl] = React.useState<string | undefined>(undefined);
React.useEffect(() => {
loadImage(file).then(setImageUrl);
}, [file]);
return (
<div>
{imageUrl && <img src={imageUrl} alt={file.name} />}
<div>{file.name}</div>
</div>
);
};
Solution 10 - Javascript
1. image with preview and close button
import React,{useState} from "react";
export default function App() {
const [preview,setPreview] = useState([]);
const maxLength = 3;
const onSelectFile=(e)=>{
for (var i=0;i < e.target.files.length;i++){
arrayofFiles.push(e.target.files[i]);
}
let images = [];
arrayofFiles.map((e)=>{
const ImageUrl = URL.createObjectURL(e);
images.push(ImageUrl)
})
setPreview(images)
}
const removeImageFromArray=(e)=>{
const index = e.target.id;
let newPreview = [...preview];
newPreview.splice(index,1);
setPreview(newPreview);
}
return (
<div className="App">
<h1>Image view and close</h1>
<input type="file" name="file" multiple onChange={onSelectFile}/>
{preview.map((img,index)=>
(
<div key={index}>
<img src={img} id={index} alt="pic1" width="250" height="250" />
<button
id={index}
key={index}
onClick={(e) => {
removeImageFromArray(e);
}}
>
Close
</button>
</div>
)
)}
</div>
);
}
Solution 11 - Javascript
Here is a good example: https://www.kindacode.com/article/react-show-image-preview-before-uploading/
const App = () => {
const [selectedImage, setSelectedImage] = useState();
const imageChange = (e) => {
if (e.target.files && e.target.files.length > 0) {
setSelectedImage(e.target.files[0]);
}
};
const removeSelectedImage = () => {
setSelectedImage();
};
return (
<>
<div>
<input
accept="image/*"
type="file"
onChange={imageChange}
/>
{selectedImage && (
<div>
<img
src={URL.createObjectURL(selectedImage)}
alt="Thumb"
/>
<button onClick={removeSelectedImage}>
Remove This Image
</button>
</div>
)}
</div>
</>
);
};
Solution 12 - Javascript
-- Constructor
constructor(props) {
super(props);
this.state = {
imgRef: ""
};
}
-- Function
filePreview(e) {
const file = e.target.files[0];
this.setState({
imgRef: URL.createObjectURL(file)
});
}
-- Html
<input
type="file"
name="photo"
onChange={this.fileSelectHandler.bind(this)}
/>
<img src={this.state.imgRef} />
Solution 13 - Javascript
URL.createObjectURL(file) short and fine solution