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

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

How to write a custom hook for uploading files in react

问题

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

  1. // custom hook useFileUpload.ts
  2. import { useState, useEffect } from 'react';
  3. import { useTranslation } from 'react-i18next';
  4. export const useFileUpload = initialValue => {
  5. const [file, setFile] = useState<File | null>(initialValue);
  6. const [error, setError] = useState(null);
  7. const { t } = useTranslation();
  8. useEffect(() => {
  9. if (file) {
  10. setError(null);
  11. }
  12. }, [file]);
  13. const handleFileChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
  14. const file = evt.target.files?.[0];
  15. const fileName = file?.name;
  16. const getFileExtension: string = fileName?.slice(
  17. (Math.max(0, fileName.lastIndexOf('.')) || Infinity) + 1
  18. ) as string;
  19. if (!file) {
  20. return;
  21. }
  22. if (!/kml|gml|dxf|png|jpg/.test(getFileExtension)) {
  23. setError(t('omgevingscheck.errors.verkeerdBestandType'));
  24. return;
  25. }
  26. setFile(file);
  27. };
  28. return [file, error, handleFileChange];
  29. };
  1. // using the custom hook here
  2. // Component with Custom Hoook
  3. const MyComponent = () => {
  4. const [file, error, handleFileChange] = useFileUpload(null);
  5. const handleUploadClick = () => {
  6. inputRef.current?.click();
  7. };
  8. return (
  9. <div>
  10. <OmgevingscheckButton
  11. onClick={handleUploadClick}
  12. text={t('omgevingscheck.btns.opladen')}
  13. secondary
  14. />
  15. <input
  16. aria-label="file-upload"
  17. className={cx(classNames)}
  18. type="file"
  19. onChange={handleFileChange}
  20. ref={inputRef}
  21. />
  22. {file && (
  23. <p>
  24. {file.name} - {file.type}
  25. </p>
  26. )}
  27. {error && <p>{error}</p>}
  28. </div>
  29. );
  30. }

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

英文:

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.

  1. const MyComponent = () =&gt; {
  2. const [file, setFile] = useState&lt;File | null&gt;(null);
  3. const [error, setError] = useState&lt;null&gt;(null);
  4. const { t } = useTranslation(&#39;app&#39;);
  5. const inputRef = useRef&lt;HTMLInputElement | null&gt;(null);
  6. useEffect(() =&gt; {
  7. if (file) {
  8. setError(null);
  9. }
  10. }, [file]);
  11. const handleUploadClick = () =&gt; {
  12. inputRef.current?.click();
  13. };
  14. const handleFileChange = (evt: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
  15. const file = evt.target.files?.[0];
  16. const fileName = file?.name;
  17. const fileExtension: string = fileName?.slice(
  18. (Math.max(0, fileName.lastIndexOf(&#39;.&#39;)) || Infinity) + 1,
  19. ) as string;
  20. if (!file) {
  21. return;
  22. }
  23. if (!/kml|gml|dxf|png|jpg/.test(fileExtension)) {
  24. setError(t(&#39;omgevingscheck.errors.verkeerdBestandType&#39;));
  25. return;
  26. }
  27. setFile(file);
  28. };
  29. const classNames = {
  30. [styles.inputIsHidden]: true,
  31. };
  32. return (
  33. &lt;div&gt;
  34. &lt;OmgevingscheckButton
  35. onClick={handleUploadClick}
  36. text={t(&#39;omgevingscheck.btns.opladen&#39;)}
  37. secondary
  38. /&gt;
  39. &lt;input
  40. aria-label=&quot;file-upload&quot;
  41. className={cx(classNames)}
  42. type=&quot;file&quot;
  43. onChange={handleFileChange}
  44. ref={inputRef}
  45. /&gt;
  46. {file &amp;&amp; (
  47. &lt;p&gt;
  48. {file.name} - {file.type}
  49. &lt;/p&gt;
  50. )}
  51. {error &amp;&amp; &lt;p&gt;{error}&lt;/p&gt;}
  52. &lt;/div&gt;
  53. );
  54. }

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.

  1. // custom hook useFileUpload.ts
  2. import { useState, useEffect } from &#39;react&#39;;
  3. import { useTranslation } from &#39;react-i18next&#39;;
  4. export const useFileUpload = initialValue =&gt; {
  5. const [file, setFile] = useState&lt;File | null&gt;(initialValue);
  6. const [error, setError] = useState&lt;null&gt;(null);
  7. const { t } = useTranslation();
  8. useEffect(() =&gt; {
  9. if (file) {
  10. setError(null);
  11. }
  12. }, [file]);
  13. const handleFileChange = (evt: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
  14. const file = evt.target.files?.[0];
  15. const fileName = file?.name;
  16. const getFileExtension: string = fileName?.slice(
  17. (Math.max(0, fileName.lastIndexOf(&#39;.&#39;)) || Infinity) + 1,
  18. ) as string;
  19. if (!file) {
  20. return;
  21. }
  22. if (!/kml|gml|dxf|png|jpg/.test(getFileExtension)) {
  23. setError(t(&#39;omgevingscheck.errors.verkeerdBestandType&#39;));
  24. return;
  25. }
  26. setFile(file);
  27. };
  28. return [file, error, handleFileChange];
  29. };
  1. // using the custom hook here
  2. // Component with Custom Hoook
  3. {/*[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;. */}
  4. const MyComponent = () =&gt; {
  5. const [file, error, handleFileChange] = useFileUpload(null);
  6. const handleUploadClick = () =&gt; {
  7. inputRef.current?.click();
  8. };
  9. return (
  10. &lt;div&gt;
  11. &lt;OmgevingscheckButton
  12. onClick={handleUploadClick}
  13. text={t(&#39;omgevingscheck.btns.opladen&#39;)}
  14. secondary
  15. /&gt;
  16. &lt;input
  17. aria-label=&quot;file-upload&quot;
  18. className={cx(classNames)}
  19. type=&quot;file&quot;
  20. onChange={handleFileChange}
  21. ref={inputRef}
  22. /&gt;
  23. {file &amp;&amp; (
  24. &lt;p&gt;
  25. {file.name} - {file.type}
  26. &lt;/p&gt;
  27. )}
  28. {error &amp;&amp; &lt;p&gt;{error}&lt;/p&gt;}
  29. &lt;/div&gt;
  30. );
  31. }

答案1

得分: 2

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

  1. (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

  1. (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:

确定