I have made a input field which allows user to upload a image.
class Form extends Component<{}, IState> {
constructor(Props) {
super(Props);
this.state = {
image: ""
};
handleImageChange (e) {
let files = e.target.files;
let reader = new FileReader();
reader.readAsDataURL(files[0]);
reader.onload = (e) => {
console.log(e.target?.result);
this.setState({image: e.target?.result})
}
}
render() {
return (
<input type = "file" onChange = {(e)=> this.handleImageChange(e)} />
<img id="target" src={this.state.image}
)}
I am able to see the uploaded image in my current Form Page. But I want to see the uploaded image in some other page. How will I do that? Do I need to use Props? If yes, how will I implement it. Any help would be appreciated.
Related
I am building an image upload form using Next.js/React.js, and I want the user to be able to assign a tag to each upload. I also want to show the image preview using 'URL.createObjectURL'. The uploading works fine, but on the upload page where I try to iterate through the list of images to show the preview and show an input box to assign the tag, none of this is showing. I cannot work out why.
The code:
import { useState } from "react";
import Head from 'next/head'
import Layout, { siteTitle } from '../../components/layout'
import Image from 'next/image'
export default function PrivatePage(props) {
const [images, setImages] = useState([])
const [imageURLS, setImageURLS] = useState([])
const [tag, setTag] = useState(null)
const uploadImageToClient = (event) => {
var imageList = images
var urlList = imageURLS
if (event.target.files && event.target.files[0]) {
imageList.push(event.target.files[0]);
urlList.push(URL.createObjectURL(event.target.files[0]))
setImages(imageList);
setImageURLS(urlList);
}
};
const uploadTagToClient = (event) => {
if (event.target.value) {
const i = event.target.value;
setTag(i);
}
};
const uploadToServer = async (event) => {
const body = new FormData()
images.map((file, index) => {
body.append(`file${index}`, file);
});
body.append("tag", tag)
const response = await fetch("/api/file", {
method: "POST",
body
});
};
return (
<Layout home>
<Head>
<title>{siteTitle}</title>
</Head>
<div className="container">
<div className="row">
<h4>Select Images</h4>
<div className="col">
<input type="file" className="btn btn-outline-success-inverse" onChange={uploadImageToClient} />
<input type="file" className="btn btn-outline-success-inverse" onChange={uploadImageToClient} />
</div>
<div className="col">
</div>
<button
className="btn btn-outline-success-inverse"
type="submit"
onClick={uploadToServer}
>
Send to server
</button>
{images.map((file, index) => {
return (
<div class="row ">
<lead>{file.name}</lead>
<input type="text" onChange={uploadTagToClient} />
<image src={imageURLS[index]}/>
</div>)
})}
</div>
</div>
</Layout>
);
}
To clarify, nothing inside images.map is showing when I select images.
The issue happens because you're mutating the arrays you have in state with imageList.push and urlList.push which React doesn't pick up. This means state doesn't actually get updated and a re-render doesn't occur.
To fix it, rather than mutating those state arrays you need to create new ones when updating them.
const uploadImageToClient = (event) => {
if (event.target.files && event.target.files[0]) {
setImages((imageList) => [...imageList, event.target.files[0]]);
setImageURLS((urlList) => [
...urlList,
URL.createObjectURL(event.target.files[0])
]);
}
};
Unrelated to the main issue, you have several minor issues inside the render part of your component, specifically inside images.map.
You need to set a key prop on the outer <div> element;
The <lead> element doesn't exist, and needs to be replaced with a valid element;
The <image> element also doesn't exist, you probably meant <img> or <Image> (from next/image).
You can handle image upload with multiple image preview with following code.
const handleFile = (e) => {
setMessage("");
let file = e.target.files;
for (let i = 0; i < file.length; i++) {
const fileType = file[i]['type'];
const validImageTypes = ['image/gif', 'image/jpeg', 'image/png'];
if (validImageTypes.includes(fileType)) {
setFile([...files,file[i]]);
} else {
setMessage("only images accepted");
}
}
};
Follow this snippet https://bbbootstrap.com/snippets/multiple-image-upload-preview-and-remove-92816546
I have an issue I could use some help with.
I have a form that allows users to upload images. After an image is uploaded, the function returns the image URL as a string, which I save using setState. This string is passed into a form field as a value.
✨✨THE ISSUE✨✨
The URL string is received and updated successfully with setState after image is uploaded. I can verify this with console.log in the following images. However! Although the form shows the value of imageUrl has changed, after the user clicks submit, the form submits the ORIGINAL value, which is set to null. How do I make it read the updated value of imageUrl?
Where imageUrl is initialized
class SubmitForm extends React.Component {
constructor(props) {
super(props);
this.state = {
imageUrl: null,
}
this.onInputchange = this.onInputchange.bind(this);
}
onInputchange(event) {
this.setState({
[event.target.name]: event.target.value
});
console.log("ONCHANGE CALLED")
}
Function that uploads image and does setState
openWidget = () => {
// create the widget
window.cloudinary.createUploadWidget(
{
cloudName: '[redacted for stack overflow]',
uploadPreset: '[redacted for stack overflow]',
},
(error, { event, info }) => {
if (event === 'success') {
this.setState({
imageUrl: info.secure_url,
})
}
},
).open(); // open up the widget after creation
};
Code that sets the value in the form field
render() {
const { imageUrl, imageAlt } = this.state;
let fRef = null;
console.log("FORM IS RENDERED")
console.log("Curent value of imageUrl is ", imageUrl)
return (
<Grid container>
...
<Button type="button" className="btn widget-btn" onClick={this.openWidget}>Upload Image</Button>
...
<TextField name='imageUrl' value={this.state.imageUrl} onChange={this.onInputchange}/>
...
...
</Grid>
);
Before an image is uploaded
After
the onChange doesnt seem to have been called? It would console log if it did.
Form submission
The field is required, and it throws an error because its reading in the ORIGINAL value of imageURL (which is null). How do I make it read the updated value of imageUrl?
I have an imageClassification function within my react component which gets the image to be classified from the img tag using document.getElementById const img = document.getElementById('animal_image');.
The image uploaded via file input updates the state which then gets passed to the the img tag
<img id="animal_image" src={profileImg} alt="a test" />
How can I update the code below so that the classifyImage function gets the image from when the image is upload via file input instead of getting from the img tag. i.e how can i access state within classifyImage function?
I looked at the filereader documentation but still could not figure out.
https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
import React, { Component } from 'react';
import * as automl from '#tensorflow/tfjs-automl';
import '#tensorflow/tfjs-backend-webgl';
import SampleDog from '../images/dogTest.jpg';
import './imageloader.css';
export class ImageLoader extends Component {
state={
profileImg: 'https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_960_720.png'
}
fileSelectedHandle = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = () =>{
if(reader.readyState === 2){
this.setState({profileImg: reader.result})
}
}
reader.readAsDataURL(file)
}
classifyImage = async () => {
const model = await automl.loadImageClassification('./image_classification_model_v1/model.json')
const img = document.getElementById('animal_image');
const predictions = await model.classify(img)
console.log('predictions', predictions)
}
render() {
const { profileImg } = this.state
return (
<div className="container">
<div className="img-main">
<h2>Image Classification Demo</h2>
<p>Upload an image of a cat or dog to check the prediction score</p>
<div >
<input
type="file"
accept="image/*"
name="image-upload"
id="input"
onChange={this.fileSelectedHandle}
/>
</div>
<div>
<img id="animal_image" src={profileImg} alt="a test" />
</div>
<button
className="img-loader-button"
onClick={() => this.classifyImage()}>Predict Score
</button>
</div>
</div>
)
}
}
export default ImageLoader
To access the state you can just do model.classify(this.state.profileImg). But that’s not gonna work directly cus tensorflow requires an image element or ImageData as input.
So I guess you’ll need to create an image element anyway.
fileSelectedHandle = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = () =>{
if(reader.readyState === 2) {
const img = document.createElement("img");
img.src = reader.result;
this.setState({ profileImg: img });
}
}
reader.readAsDataURL(file)
}
I'm trying to set up an image loader in React component, to display/render it after loading (client side only, no http requests).
The image loads up as an object, resulting in a rendering error:
"Objects are not valid as a React child"
import React, { Component } from "react";
export default class extends Component {
state = {
selectedImage: null
}
onChange = (event) => {
this.setState({
selectedImage: event.target.files[0],
})
}
render() {
return (
<div>
<div>
{this.state.selectedImage && this.state.selectedImage}
</div>
<form onSubmit={this.onFormSubmit}>
<input type="file" name="someImage" accept="image/*" onChange={this.onChange} />
<button>Add This Car</button>
</form>
</div>
)
}
}
Expected result - loaded image displayed/rendered on screen.
Actual result - Error: "Objects are not valid as a React child" ...
Uploaded file is not something that can be displayed, you'll require <img /> tag with src attribute
And the value for src attribute can be generated by reading contents of event.target.files[0]
Something like this should do:
import React, { Component } from "react";
export default class extends Component {
constructor(props) {
super(props);
this.state = {
selectedImage: null.
selectedImageContent: ""
}
}
onChange = (event) => {
const file = event.target.files[0];
const fileReader = new FileReader();
fileReader.onload = this.onFileLoad;
this.setState({
selectedImage: event.target.files[0]
})
};
onFileLoad = (e) => {
this.setState({
selectedImageContent: e.target.result
});
}
render() {
const { selectedImageContent } = this.state;
return (
<div>
<div>
{selectedImageContent && <img src={selectedImageContent} />}
</div>
<form onSubmit={this.onFormSubmit}>
<input type="file" name="someImage" accept="image/*" onChange={this.onChange} />
<button>Add This Car</button>
</form>
</div>
)
}
}
More info here
What I'm trying to do is to generate a QR code based on the Device Id to later download it. I figured out how to generate it, it shows OK on the page, everything is ok but is there any possible way to download it? (the QR image, it's a .png)
import QRCode from 'qrcode.react';
render() {
return (
<QRCode value={this.state.values.deviceId} />
)};
This is what I've done:
This is what I want to do:
I figured out how to do it, here is the code:
import QRCode from 'qrcode.react';
constructor(props) {
super(props);
this.download = this.download.bind(this);
}
componentDidMount(){
this.download()
}
render() {
return (
<div style={{display: "none"}} className="HpQrcode"> // hidden div
<QRCode
value={this.state.values._id}
size={128}
level={'H'}
/>
</div>
<a ref={(ref: any): any => this.downloadRef = ref}>
Download QR Code
</a>
)};
download() {
const canvas: any = document.querySelector('.HpQrcode > canvas');
this.downloadRef.href = canvas.toDataURL();
this.downloadRef.download = this.state.values.deviceId + "-QR.png";
}