React TypeScript:如何设置多个文件的状态?

wyn*_*007 2 typescript reactjs typescript-typings react-hooks

我面临一个问题,可能是一个非常简单的问题,但我被困了几个小时,所以我想请求你的帮助。

我有一个简单的文件输入,我想将此文件设置为稍后在提交表单时上传文件。

const [inputTattoos, setInputTattoos] = useState<[{}]>();

const handleImageChange = async ({
    currentTarget: input,
  }: React.ChangeEvent<HTMLInputElement>) => {
    if (input.files === null) return;

    console.log(input.files);

    setInputTattoos([{ ...input.files }]);
Run Code Online (Sandbox Code Playgroud)

使用这段代码,我可以将文件写入状态,但这不是我想要将其存储在状态中的方式,因为我的状态如下所示:

在此输入图像描述

我有一个数组,里面有一个带有对象的对象。我实际上从 input.files 得到的只是一个包含对象的数组,但我无法像在控制台上获取它一样存储此 input.files 。我尝试了很多解决方案,但这是我发现唯一有效的方法。使用其他解决方案时,我总是在 State 中得到一个空对象或 FileList(未定义),例如使用以下解决方案:

const [inputTattoos, setInputTattoos] = useState<FileList>()
const handleImageChange = async ({
    currentTarget: input,
  }: React.ChangeEvent<HTMLInputElement>) => {
    if (input.files === null) return;

    console.log(input.files);

setInputTattoos(input.files);
Run Code Online (Sandbox Code Playgroud)

这是怎么回事?谢谢你!

T.J*_*der 9

我只会存储文件对象,没有对象包装器或任何东西:

\n
const [inputTattoos, setInputTattoos] = useState<File[]>([]);\n// \xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92^^^^^^\xe2\x88\x92\xe2\x88\x92^^\nconst handleImageChange = ({\n  currentTarget: {files},\n}: React.ChangeEvent<HTMLInputElement>) => {\n    if (files && files.length) {\n        setInputTattoos(existing => [...existing, ...files]);\n// \xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    }\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

对此有几点说明:

\n
    \n
  1. 我已经删除了async. 更改处理程序不是async函数,并且没有任何内容会使用函数async将返回的承诺。

    \n
  2. \n
  3. 我已将files属性解构为它自己的参数,以便 TypeScript 知道它无法在防护和状态设置器回调之间进行更改。(这也很重要,因为除非您调用,否则您不应该异步访问 React 的合成事件对象的属性persist。)

    \n
  4. \n
  5. 我使用了状态设置器的回调版本。当根据状态项的现有值(在本例中为先前的内容)设置状态项时,这一点很重要。

    \n
  6. \n
  7. 上面的内容依赖于files(来自输入)可迭代,这在现代浏览器中是可迭代的,但在一些稍旧的浏览器中却不是。

    \n
  8. \n
\n

关于#4,如果您需要为稍旧的浏览器解决这个问题:

\n
const [inputTattoos, setInputTattoos] = useState<File[]>([]);\nconst handleImageChange = ({\n  currentTarget: {files},\n}: React.ChangeEvent<HTMLInputElement>) => {\n    if (files && files.length) {\n        setInputTattoos(existing => existing.concat(Array.from(files))); // *** Only change is here\n    }\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

那里的变化是回调:

\n
existing => existing.concat(Array.from(files))\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,由于filesFileList,而不是数组,因此我们需要将其转换为数组才能concat正确处理它。

\n

Array.from虽然只有几年的历史,但很容易进行多填充;如果您不想这样做,这里有一个不使用任何现代内容的替代方案(除了箭头函数):

\n
existing => existing.concat(Array.prototype.slice.call(files))\n
Run Code Online (Sandbox Code Playgroud)\n

这是用于Array.from该部分的完整示例:

\n

\r\n
\r\n
const [inputTattoos, setInputTattoos] = useState<File[]>([]);\n// \xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92^^^^^^\xe2\x88\x92\xe2\x88\x92^^\nconst handleImageChange = ({\n  currentTarget: {files},\n}: React.ChangeEvent<HTMLInputElement>) => {\n    if (files && files.length) {\n        setInputTattoos(existing => [...existing, ...files]);\n// \xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    }\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\r\n
const [inputTattoos, setInputTattoos] = useState<File[]>([]);\nconst handleImageChange = ({\n  currentTarget: {files},\n}: React.ChangeEvent<HTMLInputElement>) => {\n    if (files && files.length) {\n        setInputTattoos(existing => existing.concat(Array.from(files))); // *** Only change is here\n    }\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

这是正在运行的 TypeScript 版本

\n

  • @wyndham007 - 文件对象**以状态存储**,它们不仅仅是“{}”,正如您从显示文件名称的实时示例中可以看出的那样。至于 Blob 是什么,MDN 是你的朋友:[`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) 对象是 [`Blob`]( https://developer.mozilla.org/en-US/docs/Web/API/Blob)对象。请注意,如果您要将这些存储在状态中然后提交它们,则需要使用 [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData )在构建表单提交时,因为它可以读取文件。 (2认同)