如何解决React TypeScript:如何设置多个文件状态?
我正面临一个问题,可能是一个非常简单的问题,但是由于数小时以来我一直陷入困境,所以我想寻求您的帮助。
我有一个简单的文件输入,我想将此文件设置为陈述状态,以便稍后提交表单时上传文件。
const [inputTattoos,setInputTattoos] = useState<[{}]>();
const handleImageChange = async ({
currentTarget: input,}: React.ChangeEvent<HTMLInputElement>) => {
if (input.files === null) return;
console.log(input.files);
setInputTattoos([{ ...input.files }]);
使用此代码,我可以将文件写入状态,但这不是我想要在State上存储文件的方式,因为我的State看起来像这样:
我有一个数组,并且在其中有一个带有对象的对象。我实际上从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);
这是怎么了?谢谢!
解决方法
我只存储文件对象,不存储对象包装或任何东西:
const [inputTattoos,setInputTattoos] = useState<File[]>([]);
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^−−^^
const handleImageChange = ({
currentTarget: {files},}: React.ChangeEvent<HTMLInputElement>) => {
if (files && files.length) {
setInputTattoos(existing => [...existing,...files]);
// −−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
// ...
}
关于此的一些注意事项:
-
我已删除
async
。变更处理程序不是async
函数,也不会使用async
函数将返回的诺言。 -
我已经将
files
属性解构成它自己的参数,以便TypeScript知道它不能在保护和状态设置器回调之间更改。 (这也很重要,因为除非调用persist
,否则您不应异步访问React的合成事件对象的属性。) -
我使用了状态设置器的回调版本。当根据状态项的现有值(在本例中为先前的内容)设置状态项时,这一点很重要。
-
以上内容基于
files
(来自输入)可迭代,在现代浏览器中是可迭代的,但在某些稍旧的浏览器中则不是。
关于#4,如果您需要针对稍旧的浏览器解决此问题,则:
const [inputTattoos,setInputTattoos] = useState<File[]>([]);
const handleImageChange = ({
currentTarget: {files},}: React.ChangeEvent<HTMLInputElement>) => {
if (files && files.length) {
setInputTattoos(existing => existing.concat(Array.from(files))); // *** Only change is here
}
// ...
}
这里有回调:
existing => existing.concat(Array.from(files))
请注意,由于files
是FileList
,而不是数组,因此我们需要将其转换为concat
的数组才能正确处理。
Array.from
只有几岁了,但是很容易填充。如果您不想这样做,可以使用一种不使用任何现代方法(箭头功能除外)的替代方法:
existing => existing.concat(Array.prototype.slice.call(files))
下面是使用Array.from
的完整示例:
const { useState } = React;
function Example() {
const [inputTattoos,setInputTattoos] = useState/*<File[]>*/([]);
const [inputKey,setInputKey] = useState(0);
const handleImageChange = ({
currentTarget: {file},}/*: React.ChangeEvent<HTMLInputElement>*/) => {
if (files && files.length) {
setInputTattoos(existing => existing.concat(Array.from(files)));
}
// Reset the input by forcing a new one
setInputKey(key => key + 1);
}
return (
<form>
Selected tattoos ({inputTattoos.length}):
<ul>
{inputTattoos.map((file,index) =>
<li key={index}>{file.name}</li>
)}
</ul>
Add: <input key={inputKey} type="file" onChange={handleImageChange} />
</form>
);
}
ReactDOM.render(<Example/>,document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。