React Hooks 和 FileReader 的奇怪行为
Weird behaviour with React Hooks and FileReader
我是 React Hooks 的新手,老实说,我不确定这个问题是否与 Hooks 有关,或者我是否只是在做一些普遍错误的事情。
我想构建一个使用 HTML5 FileReader
的图片上传组件,以便在实际发布图片之前向用户展示上传的图片。
以下是我目前所拥有的。
基本上<div id="from-effect"></div>
是目前我检查图像是否可以渲染的方式。
我首先想填充此 <div>
而没有副作用(如 <div>I have {files.length} files</div>
),但这根本不会对更改做出反应。
下面带有 useEffect
的解决方案正在对变化做出反应。
但是,如果您尝试上传一些图片,您会发现它经常显示错误的结果。
function FileUploader(props) {
const [files, setFiles] = useState([]);
const loadImageContent = (name, newFiles) => {
return (e) => {
newFiles.push({ name: name, src: e.target.result });
};
}
const handleUpload = async (e) => {
const newFiles = [];
for (const file of e.target.files) {
const reader = new FileReader();
reader.onload = loadImageContent(file.name, newFiles);
await reader.readAsDataURL(file);
}
setFiles(newFiles);
}
useEffect(() => {
console.log('in use Effect, files:', files);
const prevCont = document.getElementById("from-effect");
prevCont.innerHTML = `I have ${files.length} files`;
});
return <div>
<input
type="file" name="fileUploader" id="fileUploader"
accept="image/*" multiple="multiple"
onChange={handleUpload}
/>
<div id="from-effect"></div>
</div>;
}
我做错了什么?
或者更好的是,我怎样才能在没有副作用的情况下实现它?
我不确定我是否遵循了您的最终目标,或者当您说要在 POST 上传图片之前向用户显示上传图片时您的意思 - 您是否要自动 POST,还是您希望用户单击 "upload/save/POST" 按钮或其他按钮?
下面是如何显示图像的示例:
编辑: 使事情变得更清楚,添加了 "save" 显示警报的按钮包含您可能用来 POST
返回服务器的数据。此外,为 "JSONify" 文件元数据添加了一个方法,因为我们上传文件的方式不允许我们将 [object File]
本地转换为 JSON.
const { useState } = React;
function FileUploader(props) {
const [files, setFiles] = useState([]);
const getFileMetadata = file => {
/**
* The way we are handling uploads does not allow us to
* turn the uploaded [object File] into JSON.
*
* Therefore, we have to write our own "toJSON()" method.
*/
return {
lastModified: file.lastModified,
name: file.name,
size: file.size,
type: file.type,
webkitRelativePath: file.webkitRelativePath
}
}
const handleUpload = e => {
let newstate = [];
for (let i = 0; i < e.target.files.length; i++) {
let file = e.target.files[i];
let metadata = getFileMetadata(file);
let url = URL.createObjectURL(file);
newstate = [...newstate, { url, metadata }];
}
setFiles(newstate);
};
const handleSave = () => {
alert(`POST Files Here..\n\n ${JSON.stringify(files,null,2)}`);
}
return (
<div>
<input type="file" accept="image/*" multiple onChange={handleUpload} />
<div>
<button onClick={handleSave} disabled={!(files && files.length > 0)}>
Save Image(s)
</button>
</div>
{files.map(f => {
return (
<div>
<img src={f.url} height="100" width="100" />
</div>
);
})}
</div>
);
}
ReactDOM.render(<FileUploader />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
我是 React Hooks 的新手,老实说,我不确定这个问题是否与 Hooks 有关,或者我是否只是在做一些普遍错误的事情。
我想构建一个使用 HTML5 FileReader
的图片上传组件,以便在实际发布图片之前向用户展示上传的图片。
以下是我目前所拥有的。
基本上<div id="from-effect"></div>
是目前我检查图像是否可以渲染的方式。
我首先想填充此 <div>
而没有副作用(如 <div>I have {files.length} files</div>
),但这根本不会对更改做出反应。
下面带有 useEffect
的解决方案正在对变化做出反应。
但是,如果您尝试上传一些图片,您会发现它经常显示错误的结果。
function FileUploader(props) {
const [files, setFiles] = useState([]);
const loadImageContent = (name, newFiles) => {
return (e) => {
newFiles.push({ name: name, src: e.target.result });
};
}
const handleUpload = async (e) => {
const newFiles = [];
for (const file of e.target.files) {
const reader = new FileReader();
reader.onload = loadImageContent(file.name, newFiles);
await reader.readAsDataURL(file);
}
setFiles(newFiles);
}
useEffect(() => {
console.log('in use Effect, files:', files);
const prevCont = document.getElementById("from-effect");
prevCont.innerHTML = `I have ${files.length} files`;
});
return <div>
<input
type="file" name="fileUploader" id="fileUploader"
accept="image/*" multiple="multiple"
onChange={handleUpload}
/>
<div id="from-effect"></div>
</div>;
}
我做错了什么? 或者更好的是,我怎样才能在没有副作用的情况下实现它?
我不确定我是否遵循了您的最终目标,或者当您说要在 POST 上传图片之前向用户显示上传图片时您的意思 - 您是否要自动 POST,还是您希望用户单击 "upload/save/POST" 按钮或其他按钮?
下面是如何显示图像的示例:
编辑: 使事情变得更清楚,添加了 "save" 显示警报的按钮包含您可能用来 POST
返回服务器的数据。此外,为 "JSONify" 文件元数据添加了一个方法,因为我们上传文件的方式不允许我们将 [object File]
本地转换为 JSON.
const { useState } = React;
function FileUploader(props) {
const [files, setFiles] = useState([]);
const getFileMetadata = file => {
/**
* The way we are handling uploads does not allow us to
* turn the uploaded [object File] into JSON.
*
* Therefore, we have to write our own "toJSON()" method.
*/
return {
lastModified: file.lastModified,
name: file.name,
size: file.size,
type: file.type,
webkitRelativePath: file.webkitRelativePath
}
}
const handleUpload = e => {
let newstate = [];
for (let i = 0; i < e.target.files.length; i++) {
let file = e.target.files[i];
let metadata = getFileMetadata(file);
let url = URL.createObjectURL(file);
newstate = [...newstate, { url, metadata }];
}
setFiles(newstate);
};
const handleSave = () => {
alert(`POST Files Here..\n\n ${JSON.stringify(files,null,2)}`);
}
return (
<div>
<input type="file" accept="image/*" multiple onChange={handleUpload} />
<div>
<button onClick={handleSave} disabled={!(files && files.length > 0)}>
Save Image(s)
</button>
</div>
{files.map(f => {
return (
<div>
<img src={f.url} height="100" width="100" />
</div>
);
})}
</div>
);
}
ReactDOM.render(<FileUploader />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>