I am trying to get my form to upload several files, but once I upload the first one, I have no chance to load a second one. Any Idea what I am doing wrong?
This is my upload component:
import React, { Component } from 'react'
import * as RB from 'react-bootstrap'
import Button from 'components/Button/Button'
class uploadMob extends Component {
constructor(props) {
super(props)
this.state = {
files: [],
}
}
onFilesAdded = (e) => {
const filesArray = this.state.files
filesArray.push(e.target.files[0])
this.setState({ files: filesArray })
this.uploadFiles()
}
async uploadFiles() {
this.state.files.forEach((file) => {
this.sendRequest(file)
})
}
sendRequest(file) {
const { pdfUploadToState } = this.props
pdfUploadToState(file)
}
render() {
const files = this.state.files
return (
<RB.Form.Group>
<div className="upload-btn-wrapper">
<div className="Files">
{files.map((file, key) => {
return (
<div key={key} className="Row">
<span className="Filename">
{file.name}
</span>
</div>
)
})}
</div>
<Button size="sm" variant="light">
Dateien hochladen
</Button>
<input
type="file"
name="files"
id="files"
onChange={(e) => {
this.onFilesAdded(e)
}}
multiple
/>
</div>
</RB.Form.Group>
)
}
}
export default uploadMob
The first file is uploaded perfectly, but as mentioned, the button does not respond when trying to upload a second one.
Thanks for the help!
Your code seems correct but when you use input type file with multiple attribute you need to select multiple files and then hit upload button insted of selecting files one by one.
also replace
filesArray.push(e.target.files[0])
with
for (var i = 0; i < files.length; i++)
{
filesArray.push(e.target.files[i]);
}
to upload file one by one
replace
onFilesAdded = (e) =>
{
this.state.files.push(e.target.files[0])
this.uploadFiles()
}
hope this will help you
Related
I have a React component to upload files. Below is part of the code that handles the file selection and once files are selected, it will display thumbnails.
I want to use RTL to test the file selection: clicking button which is EvidenceUploaderPanel, this opens the file selector input element, then choosing files.
This will also make the requests to upload the files as they are selected.
But I have no idea how to start.
function UploadScreen({
title,
maxNumberOfUploadFiles = 3,
acceptedFileTypes,
}: Props) {
const [documents, setDocuments] = useState<FileObject[]>([]);
const handleFileSelection = (files: FileList) => {
const documentsWithThumbnails = Array.from(files).map((file) => {
// here I also make a request to upload each file.
return {
file,
thumbnailURL: URL.createObjectURL(file),
name: file.name,
};
});
setDocuments((currentDocuments) => [...currentDocuments, ...documentsWithThumbnails]);
};
const inputRef = useRef(
(() => {
const element = document.createElement('input');
element.multiple = true;
element.type = 'file';
element.accept = acceptedFileTypes?.join(',') || IMAGE_MIME_TYPES.join(',');
element.addEventListener('change', () => {
if (element.files && documents.length + element.files.length < maxNumberOfUploadFiles) {
handleFileSelection(element.files);
element.value = '';
}
});
return element;
})(),
);
const handleOpenFileClicker = () => {
inputRef.current.click();
};
return (
<div>
<h2 className="container">{title}</h2>
{documents.length > 0 ? (
<section>
<div className="body-text">
Add files
</div>
<div className="thumbnail-container">
{documents.map((doc) => {
return (
<BaseThumbnail
src={doc?.thumbnailURL}
key={doc.name}
deleteAction={() => {
deleteDocument(doc.name);
}}
/>
);
})}
</div>
<Link onPress={handleOpenFileClicker}>
Add photos
</Link>
</section>
) : (
<section>
<div className="text">
Add files
</div>
<div className="upload-container" />
<EvidenceUploaderPanel
labelText="upload files"
openFilePicker={handleOpenFileClicker}
/>
</section>
)}
</div>
);
}
Have you thought about maybe using cypress for this?
They have a nice built in function that does exactly what you want and the setup Is really easy.
I’d recommend you using the cypress component testing for react, they have an entire page on their website’s docs explaining how to set it up. And than you can just mount the file selection component and use their cy.selectFile() method.
Good luck :)
I have two components,
Uploader Component
Which uploads the file to Azure storage
ContainerList Component
Which fetches the uploaded file's data from Azure storage.
For fetching data from azure and show in ContainerList, I referred to ListBlobs in (https://dmrelease.blob.core.windows.net/azurestoragejssample/samples/sample-blob.html)
My components are completely independent components that were imported in a different react project by using npm-link. For npm link, I referred to this documentation (https://60devs.com/simple-way-to-manage-local-node-module-using-npm-link.html)
import {Uploader, ContainerList} from 'blobuploader';
import React from 'react';
class App extends React.Component{
render(){
return (
<Uploader
accountName={azureCredentials.accountName}
sasToken={azureCredentials.sasToken}
multiple={true}/>
<ContainerList
accountName={azureCredentials.accountName}
sasToken={azureCredentials.sasToken} />
);
}
}
The main issue is when I uploaded files to Azure storage by using the Uploader Component. Container List is not updating i.e the newly uploaded file to azure storage is not reflected in ContainerList. Need to reload the page for seeing updated data in ContainerList.
For example, you can see below the image where a new file has been uploaded but not showing in the list(i.e ContainerList)
Please help me to resolve this issue.
Thanks in advance
ContainerList code
import React from 'react';
import '../css/containerList.css';
import axios from 'axios';
import PROGRESS from '../assets/progress.svg'
import PropTypes from 'prop-types';
class ContainerList extends React.Component{
constructor(props){
super(props);
this.state= {
datas: [],
};
this.blobService = null;
this.initConnections.bind(this);
}
componentDidMount(){
const script = document.createElement('script');
script.src = "https://dmrelease.blob.core.windows.net/azurestoragejssample/bundle/azure-storage.blob.js";
script.async = true;
document.body.appendChild(script);
script.onload = () => {
this.initConnections();
};
console.log('script ended');
}
initConnections = () => {
var accountName = this.props.accountName;
var SasToken = this.props.sasToken;
var blobUri = 'https://' + accountName + '.blob.core.windows.net';
this.blobService = this.blobService === null ? AzureStorage.Blob.createBlobServiceWithSas(blobUri, SasToken) : this.blobService;
this.blobService.getServiceProperties({options: ['clientRequestId']}, (error, result)=>{
if(error){
console.log('Error Creating Blob Service..')
console.log(error);
} else {
this.blobService.listBlobsSegmented('my-con', null, {include:["metadata"]}, (error, results) => {
if (error) {
console.log(error);
}
else {
var temp =[]
results.entries.map(async (ele,index) => {
console.log('Tag',ele);
var val = index+1;
var type = ele.contentSettings.contentType;
temp.push({number:val,name:ele.name,type,status:<button className='container-button'>PASS</button>,email:"testtagcheck123456#gmail.com",apiStatus:PROGRESS});
if(results.entries.length === temp.length){
this.setState({datas:temp});
}
});
}
});
}
});
}
render(){
//SHOWING DATA IN UI LIST FROM this.state.datas
return (
<div className= "table-box">
<div className="table-row-head">
{
(["No","File-Name","Type","Status","Outlook-Mail","API-Status"]).map((ele => {
return( ele === "No" ?<div className="table-cell table-head first-cell" style={{width:'4%'}}>
<p className ="heading-label">{ ele} </p>
</div> : <div className="table-cell table-head" style={{width:'20%'}}>
<p className="heading-label">{ ele} </p>
</div>)
}))
}
</div>
{
this.state.datas.map( (eles,index) => {
return (
<div className='table-row' key={index}>
<div className="table-cell first-cell" style={{width:'4%'}}>
<p>{ eles.number} </p>
</div>
<div className="table-cell" style={{width:'23%'}} >
<p>{eles.name} </p>
</div>
<div className="table-cell" style={{width:'20%'}} >
<p >{eles.type} </p>
</div>
<div className="table-cell" style={{width:'15%'}} >
<p>{eles.status} </p>
</div>
<div className="table-cell" style={{width:'23%'}}>
<p>{eles.email} </p>
</div>
<div className="table-cell" style={{width:'15%'}}>
<img src={eles.apiStatus} alt="PROGRESS" style={{width:'30%',}}/>
</div>
</div>
);
}
)
}
</div>
);
}
}
ContainerList.propTypes = {
accountName: PropTypes.string,
sasToken: PropTypes.string,
}
export default ContainerList;
Creating app to upload files from local directory, it was a successful for me. Until the part where I start add some styles and features, I've encounter some issues. after creating connection between <input> + <label>.
<input
id="file"
type="file"
name="selectedFile"
onChange={this.onChange}
/>
<label htmlFor="file">{file}</label>
The goal was to have text render on label tag instead of actual input following onChange event function.
Following ternary conditional with the fileName (from state) and file, they both are set at null as default. Since no file is been selected, the condition is set at false and text "Choose a file" will display on <label>.
render() {
const { fileName } = this.state;
let file = null;
file = fileName
? ( <span>File Selected - {fileName[0]}</span>)
: ( <span>Choose a file...</span> );
Anytime user select on <label> painted "Choose a file" text. it triggers onChange function to have file directory pop up at front of browser. Once file is selected from list, the condition becomes true. And should paint actual file name on <label> eg something.jpg...
<label htmlFor="file">{file}</label>
I didn't get any, it wasn't successful for me... However, I've strong suspect it has something to do with this syntax fileName[0]...
<span>File Selected - {fileName[0]}</span>)
I may have it wrong. Any suggestions? Thanks in advance
Here's full syntax...
export default class Form extends Component {
state = {
fileName: '',
};
onChange = e => {
switch (e.target.name) {
case 'fileName':
this.setState({ fileName: e.target.files[0] });
break;
default:
this.setState({ [e.target.name]: e.target.value });
}
};
render(){
const { fileName } = this.state;
let file = null;
file = fileName
? ( <span>File Selected - {fileName[0]}</span>)
: ( <span>Choose a file...</span> );
return(
<form onSubmit={this.onSubmit}>
<div>
<input
id="file"
type="file"
name="selectedFile"
onChange={this.onChange}
/>
<label htmlFor="file">{file}</label>
</div>
</form>
);
}
}
If I understand your question correctly, then it seems you're wanting the <label /> to display the filename of the file that was selected by the user?
This can be achieved by using the .name property on the file asscoaited with the input's change event:
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
fileName: '',
};
}
onChange = e => {
switch (e.target.name) {
// Updated this
case 'selectedFile':
if(e.target.files.length > 0) {
// Accessed .name from file
this.setState({ fileName: e.target.files[0].name });
}
break;
default:
this.setState({ [e.target.name]: e.target.value });
}
};
render(){
const { fileName } = this.state;
let file = null;
file = fileName
? ( <span>File Selected - {fileName}</span>)
: ( <span>Choose a file...</span> );
return(
<form onSubmit={this.onSubmit}>
<div>
<input
id="file"
type="file"
name="selectedFile"
onChange={ (event) => this.onChange(event) }
/> { /* Updated this to an arrow function */ }
<label htmlFor="file">{file}</label>
</div>
</form>
);
}
}
For a working example, see this jsFiddle - hope that helps!
I have a react component in which user can upload Image and he's also shown the preview of uploaded image. He can delete the image by clicking delete button corresponding to Image. I am using react-dropzone for it. Here's the code:
class UploadImage extends React.Component {
constructor(props) {
super(props);
this.onDrop = this.onDrop.bind(this);
this.deleteImage = this.deleteImage.bind(this);
this.state = {
filesToBeSent: [],
filesPreview: [],
printCount: 10,
};
}
onDrop(acceptedFiles, rejectedFiles) {
const filesToBeSent = this.state.filesToBeSent;
if (filesToBeSent.length < this.state.printCount) {
this.setState(prevState => ({
filesToBeSent: prevState.filesToBeSent.concat([{acceptedFiles}])
}));
console.log(filesToBeSent.length);
for (var i in filesToBeSent) {
console.log(filesToBeSent[i]);
this.setState(prevState => ({
filesPreview: prevState.filesPreview.concat([
<div>
<img src={filesToBeSent[i][0]}/>
<Button variant="fab" aria-label="Delete" onClick={(e) => this.deleteImage(e,i)}>
<DeleteIcon/>
</Button>
</div>
])
}));
}
} else {
alert("you have reached the limit of printing at a time")
}
}
deleteImage(e, id) {
console.log(id);
e.preventDefault();
this.setState({filesToBeSent: this.state.filesToBeSent.filter(function(fid) {
return fid !== id
})});
}
render() {
return(
<div>
<Dropzone onDrop={(files) => this.onDrop(files)}>
<div>
Upload your Property Images Here
</div>
</Dropzone>
{this.state.filesToBeSent.length > 0 ? (
<div>
<h2>
Uploading{this.state.filesToBeSent.length} files...
</h2>
</div>
) : null}
<div>
Files to be printed are: {this.state.filesPreview}
</div>
</div>
)
}
}
export default UploadImage;
My Question is my component is not re-rendering even after adding or removing an Image. Also, I've taken care of not mutating state arrays directly. Somebody, please help.
Try like this, I have used ES6
.
I'm implementing this library : https://github.com/felixrieseberg/React-Dropzone-Component
To trigger another component or element programmatically I can use refbut I got an error of photoUploadDropAreaElement is not a function using below code.
triggerUploadDialog(){
this.photoUploadDropAreaElement.click(); // doesn't work?
console.log(this.photoUploadDropAreaElement);
}
render() {
return(
<div onClick={this.triggerUploadDialog.bind(this)}>
<DropzoneComponent ref={dropArea => this.photoUploadDropAreaElement = dropArea} />
</div>
);
The result of DropzoneComponent look like this
What's wrong here? I just want to trigger a click to open the file dialog for user to select file to upload.
I'm using import * as Dropzone from 'react-dropzone'; via npm install react-dropzone --save-dev. Go here for the details.
This dropzone package allows you to, by default, click on the UI's dropzone to open the file dialog for user to select a file to upload.
Here is the code I used, which includes a 'Choose File' button as well as a 'Delete' button. *Note: multiple={false} disables the user's ability to choose multiple files. You can simply change it to true and the multiple file selection will be enabled.
import * as React from 'react';
import * as Dropzone from 'react-dropzone';
export interface FileUploadState { file: Array<File> }
export class FileUpload extends React.Component<{}, FileUploadState> {
constructor(props: any) {
super(props);
this.state = {
file: []
}
}
onDrop(droppedFile: any) {
if (droppedFile && droppedFile.preview) {
window.URL.revokeObjectURL(droppedFile.preview);
}
this.setState({
file: droppedFile
});
console.log(droppedFile);
}
onDelete() {
this.setState({
file: []
});
}
render() {
let dropzoneRef: any;
return (
<div>
<div>
<Dropzone ref={(node) => { dropzoneRef = node; }} onDrop={this.onDrop.bind(this)} multiple={false}>
<div className="text-center">Drag and drop your file here, or click here to select file to upload.</div>
</Dropzone>
<button type="button" className="button primary" onClick={(e) => {
e.preventDefault(); // --> without this onClick fires 3 times
dropzoneRef.open();
}}>
Choose File(s)
</button>
<button type="button" className="button alert" onClick={this.onDelete.bind(this)}>
Delete
</button>
</div>
<hr />
<div>
<h2>File(s) to be uploaded: {this.state.file.length} </h2>
<ul>
{
this.state.file.map(f => <li><code>{f.name}</code></li>)
}
</ul>
</div>
</div>
)
}
}
For anyone still looking, it looks like the library was updated to expose an open function.
https://github.com/react-dropzone/react-dropzone/commit/773eb660c7848dd1d67e6e13c7f7261eaaa9fd4d
You can use it this way via refs:
dropzoneRef: any;
onClickPickImage = () => {
if (this.dropzoneRef) {
this.dropzoneRef.open();
}
};
// When rendering your component, save a ref
<Dropzone
ref={(ref: any) => {
this.dropzoneRef = ref;
}}
onDrop={this.onDrop}
>
<div onClick={this.onClickPickImage}>Trigger manually</div>
</Dropzone>
Try like this. It's work for me
triggerUploadDialog () {
this.photoUploadDropAreaElement.dropzone.element.click()
}
Component
<div onClick={this.triggerUploadDialog.bind(this)}>
<DropzoneComponent ref={dropArea => this.photoUploadDropAreaElement = dropArea} />
</div>
My problem was NOT including the input element. When I did, it worked.