UNPKG

react-files-hooks

Version:

React hooks for downloading and uploading files in the browsers

143 lines (126 loc) 4.23 kB
import { useRef, useEffect, MutableRefObject } from 'react'; import { errorMessages, defaultUploadConfig } from '../constants'; const { defaultIsMultiple, defaultMaxSize } = defaultUploadConfig; /** @module Uploader */ /** * Object for inserting to html element * @interface * @global * @typedef UploadRefContainer * @property {MutableRefObject<HTMLInputElement>} ref - Reference to html element. */ /** * Object to manage uploading files * @interface * @global * @typedef UploadManager * @property {UploadRefContainer} uploader - Object for inserting to html element. * @property {function} reset - Function to reset value of input. */ /** * Uploading config * @interface * @global * @typedef UploadConfig * @property onSelectFile {function} - Function is called after uploading with uploaded file. Required * @property onError {function} - Function is called after failed downloading. Optional * @property isMultiple {boolean} - Indicator for count of uploaded files. Optional. Default - true * @property maxFiles {number} - Limit for maximum selected files. Optional. * @property maxSize {number} - Limit for maximum size of file. Optional. Default - 5242880 bytes (5MB) * @property validTypes {Array<string>} - Set of valid types for files. Optional. */ /** * Hook for uploading files * @param config {UploadConfig} - Uploading config. * @return {UploadManager} - Object to manage uploading files. * @function */ export function useUploader( { onSelectFile, onError, isMultiple = defaultIsMultiple, maxFiles, maxSize = defaultMaxSize, validTypes } = {} ) { /** * Reference to input */ const inputRef = useRef(null); /** * Reset input value */ const reset = () => { if (inputRef.current) inputRef.current.value = ''; }; /** * Handle error */ const handleError = (message) => { reset(); const error = new Error(message); if (onError && typeof onError === 'function') { return onError(new Error(message)); } }; /** * Check validity of files */ const isInvalidType = files => { if (validTypes) { for (let i = 0; i < files.length; i++) { const file = files[i]; if (!validTypes.find(type => file.type.includes(type))) return file; } } }; /** * Check max size of files */ const isMaxSize = files => { if (maxSize) { for (let i = 0; i < files.length; i++) { const file = files[i]; if (file.size > maxSize) return file; } } }; /** * Handler for input changes */ const onChange = () => { const { files } = inputRef.current; if (files && files.length) { if (maxFiles && files.length > maxFiles) { return handleError(errorMessages.amount + maxFiles); } const fileWithMaxSize = isMaxSize(files); if (fileWithMaxSize) { return handleError(fileWithMaxSize.name + errorMessages.size + maxSize); } const fileWithInvalidType = isInvalidType(files); if (fileWithInvalidType) { return handleError(fileWithInvalidType.name + errorMessages.type + fileWithInvalidType.type); } const file = files[0]; if (onSelectFile && typeof onSelectFile === 'function') { return onSelectFile(isMultiple ? files : file); } } else { return handleError(errorMessages.existingFile); } }; /** * Hook for adding/removing input listener */ useEffect(() => { const element = inputRef.current; if (element) { element.setAttribute('multiple', isMultiple); element.setAttribute('type', 'file'); element.addEventListener('change', onChange); } return () => { if (element) element.removeEventListener('change', onChange); }; }, [inputRef, isMultiple]); // eslint-disable-line return { uploader: { ref: inputRef }, reset }; }