如何编写用于在React中上传文件的自定义钩子

huangapple go评论98阅读模式
英文:

How to write a custom hook for uploading files in react

问题

以下是您要的代码翻译部分:

// 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);

  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

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>
  );
}

注意:请确保您的代码中包括正确的依赖项和引入,以便上述翻译的代码能够正确运行。

英文:

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 = () =&gt; { 
 const [file, setFile] = useState&lt;File | null&gt;(null);
  const [error, setError] = useState&lt;null&gt;(null);

  const { t } = useTranslation(&#39;app&#39;);
  const inputRef = useRef&lt;HTMLInputElement | null&gt;(null);

  useEffect(() =&gt; {
    if (file) {
      setError(null);
    }
  }, [file]);

  const handleUploadClick = () =&gt; {
    inputRef.current?.click();
  };

  const handleFileChange = (evt: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
    const file = evt.target.files?.[0];
    const fileName = file?.name;

    const fileExtension: string = fileName?.slice(
      (Math.max(0, fileName.lastIndexOf(&#39;.&#39;)) || Infinity) + 1,
    ) as string;

    if (!file) {
      return;
    }

    if (!/kml|gml|dxf|png|jpg/.test(fileExtension)) {
      setError(t(&#39;omgevingscheck.errors.verkeerdBestandType&#39;));
      return;
    }

    setFile(file);
  };

  const classNames = {
    [styles.inputIsHidden]: true,
  };

  return (
    &lt;div&gt;
      &lt;OmgevingscheckButton
        onClick={handleUploadClick}
        text={t(&#39;omgevingscheck.btns.opladen&#39;)}
        secondary
      /&gt;
      &lt;input
        aria-label=&quot;file-upload&quot;
        className={cx(classNames)}
        type=&quot;file&quot;
        onChange={handleFileChange}
        ref={inputRef}
      /&gt;
      {file &amp;&amp; (
        &lt;p&gt;
          {file.name} - {file.type}
        &lt;/p&gt;
      )}
      {error &amp;&amp; &lt;p&gt;{error}&lt;/p&gt;}
    &lt;/div&gt;
  );

}

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 &#39;react&#39;;
import { useTranslation } from &#39;react-i18next&#39;;

export const useFileUpload = initialValue =&gt; {
  const [file, setFile] = useState&lt;File | null&gt;(initialValue);
  const [error, setError] = useState&lt;null&gt;(null);

  const { t } = useTranslation();

  useEffect(() =&gt; {
    if (file) {
      setError(null);
    }
  }, [file]);

  const handleFileChange = (evt: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
    const file = evt.target.files?.[0];
    const fileName = file?.name;

    const getFileExtension: string = fileName?.slice(
      (Math.max(0, fileName.lastIndexOf(&#39;.&#39;)) || Infinity) + 1,
    ) as string;

    if (!file) {
      return;
    }

    if (!/kml|gml|dxf|png|jpg/.test(getFileExtension)) {
      setError(t(&#39;omgevingscheck.errors.verkeerdBestandType&#39;));
      return;
    }

    setFile(file);
  };

  return [file, error, handleFileChange];
};
// using the custom hook here
// Component with Custom Hoook

 {/*[ERROR]: Property &#39;type&#39; does not exist on type &#39;File | ((evt: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; void)&#39;. Property &#39;type&#39; does not exist on type &#39;(evt: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; void&#39;. */}

const MyComponent = () =&gt; { 

const [file, error, handleFileChange] = useFileUpload(null);

  const handleUploadClick = () =&gt; {
    inputRef.current?.click();
  };

  return (
    &lt;div&gt;
      &lt;OmgevingscheckButton
        onClick={handleUploadClick}
        text={t(&#39;omgevingscheck.btns.opladen&#39;)}
        secondary
      /&gt;
      &lt;input
        aria-label=&quot;file-upload&quot;
        className={cx(classNames)}
        type=&quot;file&quot;
        onChange={handleFileChange}
        ref={inputRef}
      /&gt;
      {file &amp;&amp; (
        &lt;p&gt;
          {file.name} - {file.type} 
        &lt;/p&gt;
      )}
      {error &amp;&amp; &lt;p&gt;{error}&lt;/p&gt;}
    &lt;/div&gt;
  );
}

答案1

得分: 2

这只是一个打字错误,
TypeScript 将您的 hook 的返回类型概括为

(File | ((evt: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; void))[]

(一个数组,其中每个项都是 File 或函数)

您可能想要显式地为您的自定义 hook 指定返回类型

export const useFileUpload = (initialValue): [File, string, ((evt: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; void)] =&gt; { ... }

(由 File、字符串和函数组成的元组)

英文:

It is just a typing error,
Typescript is generalizing the return type of your hook as

(File | ((evt: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; void))[]

(An array where each item is either a File or a function)

You probably want to explicity type the return type of your custom hook


export const useFileUpload = (initialValue): [File, string, ((evt: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; void)] =&gt; {
...
}

(A tuple composed of a File, a string and a function)

huangapple
  • 本文由 发表于 2023年3月9日 16:46:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/75682219.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定