How to write a custom hook for uploading files in react
I'm trying to create a custom react hook to get the value of an input.change for a file upload since I want to reuse the functionality that handles the file upload useEffect, useState , file upload, onChange..etc in different places.
I have extracted the code to a custom hook but I'm getting the following "errors" on the UI
- I cannot see the error message..
- I cannot get the file name..
- I get 2 typescript errors (which explains error #1 and error #2 ?)
This is the actual code and the component in its original state. (prior to refactoring to a custom hook). This works as expected.
const MyComponent = () => {
const [file, setFile] = useState<File | null>(null);
const [error, setError] = useState<null>(null);
const { t } = useTranslation('app');
const inputRef = useRef<HTMLInputElement | null>(null);
useEffect(() => {
if (file) {
setError(null);
}
}, [file]);
const handleUploadClick = () => {
inputRef.current?.click();
};
const handleFileChange = (evt: ChangeEvent<HTMLInputElement>) => {
const file = evt.target.files?.[0];
const fileName = file?.name;
const fileExtension: string = fileName?.slice(
(Math.max(0, fileName.lastIndexOf('.')) || Infinity) + 1,
) as string;
if (!file) {
return;
}
if (!/kml|gml|dxf|png|jpg/.test(fileExtension)) {
setError(t('omgevingscheck.errors.verkeerdBestandType'));
return;
}
setFile(file);
};
const classNames = {
[styles.inputIsHidden]: true,
};
return (
<div>
<OmgevingscheckButton
onClick={handleUploadClick}
text={t('omgevingscheck.btns.opladen')}
secondary
/>
<input
aria-label="file-upload"
className={cx(classNames)}
type="file"
onChange={handleFileChange}
ref={inputRef}
/>
{file && (
<p>
{file.name} - {file.type}
</p>
)}
{error && <p>{error}</p>}
</div>
);
}
When I try to refactor the hook into a custom hook and use it I lose the functionality. I'm misunderstanding the use of a custom hook somewhere.
// custom hook useFileUpload.ts
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
export const useFileUpload = initialValue => {
const [file, setFile] = useState<File | null>(initialValue);
const [error, setError] = useState<null>(null);
const { t } = useTranslation();
useEffect(() => {
if (file) {
setError(null);
}
}, [file]);
const handleFileChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
const file = evt.target.files?.[0];
const fileName = file?.name;
const getFileExtension: string = fileName?.slice(
(Math.max(0, fileName.lastIndexOf('.')) || Infinity) + 1,
) as string;
if (!file) {
return;
}
if (!/kml|gml|dxf|png|jpg/.test(getFileExtension)) {
setError(t('omgevingscheck.errors.verkeerdBestandType'));
return;
}
setFile(file);
};
return [file, error, handleFileChange];
};
// using the custom hook here
// Component with Custom Hoook
{/*[ERROR]: Property 'type' does not exist on type 'File | ((evt: ChangeEvent<HTMLInputElement>) => void)'. Property 'type' does not exist on type '(evt: ChangeEvent<HTMLInputElement>) => void'. */}
const MyComponent = () => {
const [file, error, handleFileChange] = useFileUpload(null);
const handleUploadClick = () => {
inputRef.current?.click();
};
return (
<div>
<OmgevingscheckButton
onClick={handleUploadClick}
text={t('omgevingscheck.btns.opladen')}
secondary
/>
<input
aria-label="file-upload"
className={cx(classNames)}
type="file"
onChange={handleFileChange}
ref={inputRef}
/>
{file && (
<p>
{file.name} - {file.type}
</p>
)}
{error && <p>{error}</p>}
</div>
);
}
Comments
Post a Comment