Render file that was just uploaded in react - reactjs

I want to immediately display the user a file he/she just uploaded in ReactJS. Currently, I am using this code. The image file is not being rendered in the img tag.
( <--PS this is not an issue in image rendering issue in StackOverflow or on your browser. This is how the looks)
import { useState } from 'react';
export default function Upload() {
const [uploadedFile, setuploadedFile] = useState(null);
return (
<div>
<label htmlFor='upload-design'>
{uploadedFile ?
<img src={uploadedFile} /> :
<div>{/* some HTML here*/}</div>}
</label>
<input id='upload-design' type='file' onChange={e => setuploadedFile(e.target.files[0])} />
</div>
)
}

You can't directly show the file input value in <img src=''/> tag.
First you need to convert file input value into base64 then show in <img /> tag
Try below code it's works !
function App() {
const [uploadedFile, setuploadedFile] = useState(null);
const base64FileURL = (element, callback) => {
let file = element;
let reader = new window.FileReader();
reader.onloadend = function (e) {
callback(e.target.result);
};
reader.readAsDataURL(file);
}
const handleFileChange = (file) => {
base64FileURL(file, (obj) => {
setuploadedFile(obj);
});
}
return (
<div>
<label htmlFor='upload-design'>
{uploadedFile ?
<img src={uploadedFile} /> :
<div>{/* some HTML here*/}</div>}
</label>
<input id='upload-design' type='file' onChange={e => handleFileChange(e.target.files[0])} />
</div>
);
};
export default App;

Related

React Duplicate components updating wrong state: hooks

I'm a newbie to react, only been using it for a few days, so forgive me if this is a stupid question.
I have a file input component and an image thumbnail component, I use two duplicate file input components to update two different states then display the image from the different states in two different thumbnail components. I have unique keys set on all of the components, but only the state for the first component in the Dom is updated. When I add an image using the second file input, it updates the state belonging to the first file input.
I've tried looking for solutions and all of them state to use unique keys, which I think I have done properly.
let [certification, setCertification] = useState(null)
let [photoId, setPhotoId] = useState(null)
let handleUpdateCertificate = (e) =>{
let file = e.target.files[0]
console.log(file)
let path = URL.createObjectURL(file)
let newCertificate = {
'file': file,
'path' : path
}
setCertification(newCertificate)
}
let handleUpdatePhotoId = (e) => {
let file = e.target.photoidinput.files[0]
let path = URL.createObjectURL(file)
let newPhotoID = {
'file': file,
'path' : path
}
setPhotoId(newPhotoID)
}
My return html is:
<div className='justify-content-center margin-20' key='certificate-wrapper'>
<ImgThumbnail key={'certificate'} name={'certificate'} image=
{certification?.path} wrapperClass={'justify-content-center margin-20'}/>
</div>
<div className='justify-content-center margin-20'>
<FileInput key={'certificateinput'} name={'certificateinput'} labelText={<p
className='text-paragraph edit-btn-text'>Add Certificate</p>}
onChange={handleUpdateCertificate}
classWrapper={'edit-profile-responsive-btn-wrapper'}/>
</div>
<div className='justify-content-center margin-20 ' key='photo-Id'>
<ImgThumbnail key={'photoid'} name={'photoId'} image={photoId?.path}
wrapperClass={'justify-content-center margin-20'}/>
</div>
<div className='justify-content-center margin-20' key='photo-id-input-wrapper'>
<FileInput key={'photoidinput'} name={'photoidinput'} labelText={<p
className='text-paragraph edit-btn-text'>Add Photo ID</p>}
onChange={handleUpdatePhotoId}
classWrapper={'edit-profile-responsive-btn-wrapper'}/>
</div>
Okay I'll give you some hints and then give you the working example:
You don't need to set key attribute if you are writing JSX elements like that, you need that only if you render a list of elements from an array, to prevent useless re-rendering when the array updates.
use const instead of let when a variable is static, there is a lint rule about it !
Try to use DRY, your update Handlers share a lot of logic, if you are going to add more inputs that would be all code repetition.
Now the code:
import React, { useState } from 'react';
import './style.css';
export default function App() {
const [certification, setCertification] = useState(null);
const [photoId, setPhotoId] = useState(null);
const updateData = (file, cb) => {
const path = URL.createObjectURL(file);
const data = {
file: file,
path: path,
};
cb(data);
};
const handleUpdateCertificate = (e) => {
updateData(e.target.files[0], setCertification);
};
const handleUpdatePhotoId = (e) => {
updateData(e.target.files[0], setPhotoId);
};
return (
<div>
{certification && (
<div className="justify-content-center margin-20">
<ImgThumbnail
name={'certificate'}
image={certification?.path}
wrapperClass={'justify-content-center margin-20'}
/>
</div>
)}
<div className="justify-content-center margin-20">
<FileInput
id="certificate"
name={'certificateinput'}
labelText={
<p className="text-paragraph edit-btn-text">Add Certificate</p>
}
onChange={handleUpdateCertificate}
classWrapper={'edit-profile-responsive-btn-wrapper'}
/>
</div>
{photoId && (
<div className="justify-content-center margin-20 " key="photo-Id">
<ImgThumbnail
name={'photoId'}
image={photoId?.path}
wrapperClass={'justify-content-center margin-20'}
/>
</div>
)}
<div
className="justify-content-center margin-20"
key="photo-id-input-wrapper"
>
<FileInput
id="photo"
name={'photoidinput'}
labelText={
<p className="text-paragraph edit-btn-text">Add Photo ID</p>
}
onChange={handleUpdatePhotoId}
classWrapper={'edit-profile-responsive-btn-wrapper'}
/>
</div>
</div>
);
}
const FileInput = ({ id, labelText, ...props }) => (
<label htmlFor={id}>
{labelText}
<input id={id} style={{ display: 'none' }} type="file" {...props} />
</label>
);
const ImgThumbnail = ({ name, image }) => (
<div>
<img style={{ width: '100px', height: '100px' }} src={image} alt={name} />
</div>
);
This example works right, you were probably doing something wrong inside FileInput Component, remember that a label has to have an htmlFor attribute with the id of the input element you want to trigger.
Now, this code can be optimized and made more React style, since you might have more file inputs in the future, let's see how it can be optimized by creating reusable Components and compose them properly:
import React, { useState } from 'react';
import './style.css';
/* INPUTS IMAGE TYPES */
const inputs = [
{ type: 'photo', name: 'photo', label: 'Photo' },
{ type: 'certificate', name: 'certificate', label: 'Certificate' },
{ type: 'anotherType', name: 'anotherName', label: 'Another Input' },
];
export default function App() {
return (
<div>
{inputs.map((data) => (
<ImagePreviewer key={data.type} data={data} />
))}
</div>
);
}
const FileInput = ({ id, labelText, ...props }) => (
<label htmlFor={id}>
{labelText}
<input id={id} style={{ display: 'none' }} type="file" {...props} />
</label>
);
const ImgThumbnail = ({ name, image }) => (
<div>
<img src={image} alt={name} />
</div>
);
const ImagePreviewer = ({ data: { type, name, label } }) => {
const [image, setImage] = useState(null);
const updateData = (file, cb) => {
const path = URL.createObjectURL(file);
const data = {
file: file,
path: path,
};
cb(data);
};
const handleUpdate = (e) => {
updateData(e.target.files[0], setImage);
};
return (
<div>
{image && (
<div>
<ImgThumbnail name={'name'} image={image?.path} />
</div>
)}
<div>
<FileInput
id={name}
name={name}
labelText={<p>Add {label}</p>}
onChange={handleUpdate}
/>
</div>
</div>
);
};
A working demo HERE.

Rerender sibling component in React

I am new to React. I am stuck on this problem for days now.
I have got a parent component which wraps two sibling components, "FileUpload" and "Documents"
The "FileUpload" is for uploading a file and "Documents" is for displaying all the uploaded files.
I want the "Documents" rerender after a new file is uploaded via "FileUpload", so that it shows the new file in the UI.
What would be the best approach to achieve this ?
Below is the code I have written so far for the sibling components:
FileUpload:
import React, { useState } from "react";
import Axios from "axios";
const FileUpload = (props) => {
const [files, setFiles] = useState([]);
const onInputChange = (e) => {
setFiles(e.target.files);
};
const handleSubmit = async (e) => {
e.preventDefault();
const data = new FormData();
for (let i = 0; i < files.length; i++) {
// console.log(files);
data.append("file", files[i]);
}
data.append("parentDbId", props.parentDbId);
data.append("parentObject", props.parentObject);
//console.log(data);
try {
await Axios.post("http://localhost:5000/upload", data);
} catch (err) {
console.error(err.message);
}
};
return (
<form
// action="http://localhost:5000/upload"
// method="POST"
//encType="multipart/form-data"
onSubmit={handleSubmit}
>
<div className="row mb-3">
<div className="col-lg-4">
<label htmlFor="formFileMultiple" className="form-label mb-0">
Add files
</label>
<input
className="form-control"
type="file"
id="formFileMultiple"
name="file"
multiple
onChange={onInputChange}
/>
</div>
<div className="col-lg-4 mt-0 gx-0">
<button type="submit" className="btn btn-primary mt-4">
Upload
</button>
</div>
</div>
</form>
);
};
export default FileUpload;
====================================================================
Documents:
import React, { useState, useEffect } from "react";
import axios from "axios";
const Documents = (props) => {
const parentDbId = props.parentDbId;
const [documents, setDocuments] = useState([]);
//mount
useEffect(() => {
console.log("first use effect");
loadDocuments();
}, []);
const loadDocuments = async () => {
const result = await axios.get(
`http://localhost:5000/documents/${parentDbId}`
);
setDocuments(result.data);
};
return (
<>
<div className="row">
{documents.map((document, index) => (
<div className="col-lg-3" key={index}>
<a href={document.filePath}>{document.fileName}</a>
</div>
))}
</div>
</>
);
};
export default Documents;
Thanks,
Jimmy
Simple, just have the parent control document state and pass the state and callback down to the children as a prop. Now the siblings are referencing the same state and will be re-rendered when props (ie document state) changes. The parent can also handle the data fetching and uploading.
it will look like this:
const Parent = () => {
const [documents, setDocuments] = useState([]);
...do data fetching here
const handleSubmit = useCallback(async () => {}, []); // You might want to reset document state here?
return (
<div>
<Docs documents={documents} />
<Upload onUpload={setDocuments} onSubmit={handleSubmit} />
</div>
);
}
I wonder if you should actually have two documents components, one for displaying the files being uploaded, and one for displaying the already uploaded files. You would embed one within the Upload component and the other would fetch documents from the api every time onUpload completes

Displaying an image of a file from input

I am trying to show an image of a file that was selected from input without sending data to the server.
Wanted to do it with URL.createObjectUrl.
const onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
this.setState({
image: URL.createObjectURL(event.target.files[0])
});
}
}
<input type="file"
onChange={onImageChange}
className="filetype"
id="group_image"/>
And then pass an image inside
<img
id="target"
src={this.state.image}/>
But i get undefined has no properties with this code. So does anyone know how to do it?
EDIT: https://codesandbox.io/s/restless-sun-61uui?file=/src/App.js
Full code:
import React from 'react'
const Addfile = () => {
const onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
this.setState({
image: URL.createObjectURL(event.target.files[0])
});
}
}
return (
<div>
<input type="file"
onChange={onImageChange}
className="filetype"
id="group_image"/>
<img
id="target"
src={this.state.image}/>
</div>
)
}
export default Addfile
You are using a functional component. There is not this.state, isn't it?
You should use useState. I updated the codesandbox for you: https://codesandbox.io/s/zealous-noether-3tz4f?file=/src/App.js

Preview image upload with React Functions

hello im trying to make a image upload and preview but i can only find react tutorials on how to do this using class components
import React from 'react';
const AddNew = () => {
const fileHandler = (event) => {
console.log(event.target.files[0])
}
const alt = (event) => {
return(event.target.files[0].name)
}
const preview = (event) => {
return (
URL.createObjectURL(event.target.files[0])
)
}
return (
<div className="addNew">
<img src={preview} alt={alt}/>
<input type="file" onChange={fileHandler} />
</div>
)
}
export default AddNew
how do i preview it using this syntax?
i get an error for invalid values for props 'src' and 'alt'
You need to use state to let React know when to re-render. You can use useState hook to save your component state and file information, and when it changes, React knows it's the time to render.
const AddNew = ({}) => {
const [file, setFile] = React.useState(null)
const fileHandler = (e) => {
setFile(e.target.files[0])
}
return (
<div>
<img src={file? URL.createObjectURL(file) : null} alt={file? file.name : null}/>
<input type="file" onChange={fileHandler} />
</div>
)
}
ReactDOM.render(<AddNew />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"/>
Use a bit of state to provide an initial value for src and alt and hold the updated values.
const initialState = { alt: "", src: "" };
const AddNew = () => {
const [{ alt, src }, setPreview] = useState(initialState);
const fileHandler = event => {
const { files } = event.target;
setPreview(
files.length
? {
src: URL.createObjectURL(files[0]),
alt: files[0].name
}
: initialState
);
};
return (
<div className="addNew">
<img className="preview" src={src} alt={alt} />
<input accept="image/*" type="file" onChange={fileHandler} />
</div>
);
};
You need to use FileReader to convert the image to base64 string. Assuming you are doing a single file upload, just maintain a state for eg: file and set the state after the filereader onload listener is done reading image.
Working copy of your code is here:
import React, { useState } from "react";
const AddNew = () => {
const [file, setFile] = useState(null);
const fileHandler = event => {
console.log(event.target.files[0]);
let reader = new FileReader();
reader.onload = function(e) {
setFile(e.target.result);
};
reader.readAsDataURL(event.target.files[0]);
};
return (
<div className="addNew">
<img src={file} alt={""} />
<input type="file" onChange={fileHandler} />
</div>
);
};
export default AddNew;

How to upload an image in React JS?

<div className="mb-1">
Image <span className="font-css top">*</span>
<div className="">
<input type="file" id="file-input" name="ImageStyle"/>
</div>
</div>
This is the snippet i provided that i was using to pick the file from the device in react js,
Using this i can select the file and that filename is also shown as well
What i want is now to store this file on S3 or anywhere and get its URL from there and POST it to my server using fetch api call.
import React, { useState } from "react";
const UploadAndDisplayImage = () => {
const [selectedImage, setSelectedImage] = useState(null);
return (
<div>
<h1>Upload and Display Image usign React Hook's</h1>
{selectedImage && (
<div>
<img
alt="not found"
width={"250px"}
src={URL.createObjectURL(selectedImage)}
/>
<br />
<button onClick={() => setSelectedImage(null)}>Remove</button>
</div>
)}
<br />
<br />
<input
type="file"
name="myImage"
onChange={(event) => {
console.log(event.target.files[0]);
setSelectedImage(event.target.files[0]);
}}
/>
</div>
);
};
export default UploadAndDisplayImage;
Upload the image from your file and display it on your page in react,
you can also get the image object in the state when we select the image
to display on the webpage you have to convert the image object to object using URL.createObjectURL(fileObject)
import React, { Component } from "react";
class DisplayImage extends Component {
constructor(props) {
super(props);
this.state = {
image: null
};
// if we are using arrow function binding is not required
// this.onImageChange = this.onImageChange.bind(this);
}
onImageChange = event => {
if (event.target.files && event.target.files[0]) {
let img = event.target.files[0];
this.setState({
image: URL.createObjectURL(img)
});
}
};
render() {
return (
<div>
<div>
<div>
<img src={this.state.image} />
<h1>Select Image</h1>
<input type="file" name="myImage" onChange={this.onImageChange} />
</div>
</div>
</div>
);
}
}
export default DisplayImage;
If you want to upload image and post it to an API. Then you install react-image-uploader. It saves the image to your local port and also in your database by raising a POST request.
This code let you upload image to the server,the backend code is written in nestjs,and display the image which will be uploaded.I have used the formdata.
import React, { useEffect, useState } from "react";
function Product() {
const { REACT_APP_REST } = process.env;
const [file, setFile] = useState([]);
const handleFile = event => {
setFile(
URL.createObjectURL(event.target.files[0])
);
const formData = new FormData();
formData.append("fileupload", event.target.files[0]);
fetch(REACT_APP_REST + "/product/upload", {
method: 'POST',
body: formData,
dataType: "jsonp"
})
};
return (
<>
<Container fluid>
<Col md="4">
<Card className="card-user">
<img src={file} />
<Card.Body>
<Form.Group>
<label>IMAGE</label>
<Form.Control
type="file"
required="required"
onChange={handleFile}
></Form.Control>
</Form.Group>
</Card.Body>
<hr></hr>
</Card>
</Col>
</Container>
</>
);
}
export default Product;
using react-uploady you can do this very easily:
import React from "react";
import Uploady from "#rpldy/uploady";
import UploadButton from "#rpldy/upload-button";
import UploadPreview from "#rpldy/upload-preview";
const filterBySize = (file) => {
//filter out images larger than 5MB
return file.size <= 5242880;
};
const App = () => (
<Uploady
destination={{ url: "my-server.com/upload" }}
fileFilter={filterBySize}
accept="image/*"
>
<UploadButton />
<UploadPreview />
</Uploady>
);
Failed to execute 'createObjectURL' on 'URL': Overload resolution failed.
For some reason I coudn't use URL.createObjectURL(image) as
const [image, setImage] = useState(null);
const [imgURL, setImgURL] = useState();
<img src={URL.createObjectURL(image)}/>
So I save the Url in the state for instant display on the button click method. This worked!
setImgURL(URL.createObjectURL(image));
Unfortunately, I was still getting the same error when I use useEffect.
useEffect(() => {
setImgURL(URL.createObjectURL(image));
}, [image]);

Resources