Estoy tratando de construir un componente de carga de selección única simple. Al hacer clic en un botón que tiene un campo de entrada oculto, abro el cuadro de diálogo del archivo y luego selecciono un archivo. El marcador de posición cambia al nombre del archivo y luego a un botón para cambiar y borrar. Todo funciona bien, pero al hacer clic en Borrar el cuadro de diálogo del archivo, no quiero que se abra en Borrar. Debería abrirse cuando se hace clic en "Elegir archivo para cargar" y "Cambiar". ¿Alguien puede ayudar?.
Estoy usando material UI para el mismo
Caja de arena: 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> </> ); };
Debe evitar el comportamiento predeterminado del evento. Me funcionó así:
<span onClick={(e) => { e.preventDefault(); setSelectedFiles(undefined); }}>Clear</span>
useRef
hook para referirme al campo de entrada oculto, algo como esto, por ejemplo:
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> </> ); }
Ver demostración de trabajo: https://stackblitz.com/edit/react-dmzlsq?file=demo.js