Reputation:
I am trying to build a simple single select upload component. On click of a button that has a hidden input field, I open the file dialog then I select a file. The placeholder changes to the file name and then a change and clear button. Everything works fine, but on click of clear the file dialog, I dont want it to open on clear. It should open when "Choose file to upload" and "Change" is clicked. Can someone help?.
I am using material UI for the same
Sandbox: https://codesandbox.io/s/react-hook-upload-oxqdp2?file=/src/Upload.tsx:0-1784
import * as React from "react";
import { Button } from "@material-ui/core";
import { useState } from "react";
interface UploaderProps {
fileType?: string | AcceptedFileType[];
}
enum AcceptedFileType {
Text = ".txt",
Gif = ".gif",
Jpeg = ".jpg",
Png = ".png",
Doc = ".doc",
Pdf = ".pdf",
AllImages = "image/*",
AllVideos = "video/*",
AllAudios = "audio/*"
}
export const Upload = (props: UploaderProps) => {
const { fileType } = props;
const acceptedFormats: string | AcceptedFileType[] =
typeof fileType === "string"
? fileType
: Array.isArray(fileType)
? fileType?.join(",")
: AcceptedFileType.Text;
const [selectedFiles, setSelectedFiles] = useState<File | undefined>(
undefined
);
const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
setSelectedFiles(event?.target?.files?.[0]);
};
const onUpload = () => {
console.log(selectedFiles);
};
return (
<>
<Button
variant="contained"
component="label"
style={{ textTransform: "none" }}
>
<input
hidden
type="file"
accept={acceptedFormats}
onChange={handleFileSelect}
/>
{!selectedFiles?.name && <span> Choose file to upload</span>}
{selectedFiles?.name && (
<>
<span style={{ float: "left" }}> {selectedFiles?.name}</span>
<span style={{ padding: "10px" }}> Change</span>
<span onClick={() => setSelectedFiles(undefined)}>Clear</span>
</>
)}
</Button>
<Button
color="primary"
disabled={!selectedFiles}
style={{ textTransform: "none" }}
onClick={onUpload}
>
Upload
</Button>
</>
);
};
Upvotes: 1
Views: 5826
Reputation: 1370
I would use useRef
hook to refer to the hidden input field, something like this for example:
import * as React from 'react';
import Button from '@mui/material/Button';
const AcceptedFileType = {
Text: '.txt',
Gif: '.gif',
Jpeg: '.jpg',
Png: '.png',
Doc: '.doc',
Pdf: '.pdf',
AllImages: 'image/*',
AllVideos: 'video/*',
AllAudios: 'audio/*',
};
export default function Upload({ fileType }) {
const fileRef = React.useRef();
const acceptedFormats =
typeof fileType === 'string'
? fileType
: Array.isArray(fileType)
? fileType?.join(',')
: AcceptedFileType.Text;
const [selectedFiles, setSelectedFiles] = React.useState();
const handleFileSelect = (event) => {
setSelectedFiles(event?.target?.files?.[0]);
};
const onUpload = () => {
console.log(selectedFiles);
};
const onClear = () => {
setSelectedFiles(undefined);
};
const onUpdate = (event) => {
if (event.target.textContent.trim().toLowerCase() === 'change') {
onClear();
fileRef.current.click();
return;
}
if (event.target.textContent.trim().toLowerCase() === 'clear') {
onClear();
return;
}
};
return (
<>
<input
ref={fileRef}
hidden
type="file"
accept={acceptedFormats}
onChange={handleFileSelect}
/>
{!selectedFiles?.name && (
<Button
variant="contained"
component="label"
style={{ textTransform: 'none' }}
onClick={() => fileRef.current?.click()}
>
Choose file to upload
</Button>
)}
{selectedFiles?.name && (
<Button
variant="contained"
component="label"
style={{ textTransform: 'none' }}
onClick={onUpdate}
>
<span style={{ float: 'left' }}> {selectedFiles?.name}</span>
<span style={{ padding: '10px' }}> Change</span>
<span>Clear</span>
</Button>
)}
<Button
color="primary"
disabled={!selectedFiles}
style={{ textTransform: 'none' }}
onClick={onUpload}
>
Upload
</Button>
</>
);
}
See working demo: https://stackblitz.com/edit/react-dmzlsq?file=demo.js
Upvotes: 0
Reputation: 149
You should prevent default behavior of event. It worked for me like this:
<span onClick={(e) => { e.preventDefault(); setSelectedFiles(undefined); }}>Clear</span>
Upvotes: 0