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

  1. I cannot see the error message..
  2. I cannot get the file name..
  3. 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

Popular posts from this blog

Spring Elasticsearch Operations

Network Error and Timeout on Authorize.net JS

Object oriented programming concepts (OOPs)