我正在尝试将对象{link:href}
推入项目中的状态。当我尝试在{link:href}
中添加href时,它说未定义:
这是我的代码:
class Download extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.inputRef = React.createRef();
this.state = {
files: []
};
}
handleClick = () => {
const node = this.inputRef.current;
const self = this;
let file;
let name;
let href;
node.addEventListener("change", function() {
const fileList = [];
for (let x = 0, xlen = this.files.length; x < xlen; x++) {
file = this.files[x];
name = file.name;
fileList.push({ name: name });
let reader = new FileReader();
reader.onload = e => {
href = e.target.result;
};
fileList.push({ link: href });
reader.readAsDataURL(file);
}
self.setState({ files: fileList });
console.log(self.state);
});
};
render() {
return (
<div className="input">
<input
onClick={this.handleClick}
id="upload-file"
className="inputName"
type="file"
multiple
ref={this.inputRef}
/>
<div>
<ul ref={this.ulRef}>
{this.state.files.map((file, index) => (
<li key={index}>
<Link to={file.link}>{file.name}</Link>
</li>
))}
</ul>
</div>
</div>
);
}
}
export default Download;
我尝试在reader.onload函数之后推送{link:href}
的原因是,因为for循环运行了x次之后,reader.onload函数才被加载,并且仅显示最后一项。
答案 0 :(得分:0)
第一件事,您不需要使用任何refs
。而且,您不需要向node
添加事件侦听器(根本不需要node
),因为可以使用react的onChange
而不是onClick
。您也不需要为此使用Link
。您可以仅使用带有href的锚标记,可以将文件数据分配给该标记。我更喜欢使用处理下载的功能,因为我觉得它可以提供更多控制权,并且不会影响浏览器的窗口或DOM元素。本质上,您可以创建一个锚标记,触发下载然后将其删除。
您还错误地将元素推入。通过推入{name: filename}
然后推入{link: href}
,您刚刚将两个对象推入了数组,并且这两个对象都将被映射。而是创建一个对象,然后将这两个键值对都添加到该对象中并将该单个对象推入。在到达reader.onload
之前,无需将任何东西推入数组。通过将某物推入reader.onload
之前,然后再将其推入reader.onload
中,您只是将某物推入两次。
将name
添加到对象。删除第一个推送,然后在reader.onload
中将link
值添加到同一对象,然后将该对象推送到数组中。您也不应在reader.onload
之外设置状态,因为这会触发重新渲染并渲染不完整的对象,从而导致错误。仅在reader.onload
中设置一次状态,以便在触发重新渲染时它将在列表中具有完整的对象。
如果您希望文件持久存在(意味着允许将多个文件添加到文件列表中),则不必将fileList
每次触发时都将onChange
设置为空数组,将其设置为{ {1}}。我们可以使用this.state.files.slice()
来创建一个新数组,而不能直接更改slice
。
Here is a working code sandbox。
如您所见,我也删除了您的this.state.files
,因为仅使用constructor
就足够了。如果您喜欢使用state = {...}
,可以根据需要将其添加回去,我只是为了节省几行代码而已。
constructor
class Download extends React.Component {
state = {
files: []
};
handleClick = event => {
let file, name, href;
var breh = event.target;
const fileList = this.state.files.slice();
for (let x = 0, xlen = breh.files.length; x < xlen; x++) {
file = breh.files[x];
name = file.name;
var obj = {};
obj["name"] = name;
let reader = new FileReader();
reader.onload = e => {
href = e.target.result;
obj["link"] = href;
fileList.push(obj);
this.setState({ files: fileList });
};
reader.readAsDataURL(file);
}
};
download = (uri, name) => {
var link = document.createElement("a");
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
render() {
console.log(this.state);
return (
<div className="input">
<input
onChange={this.handleClick}
id="upload-file"
className="inputName"
type="file"
multiple
/>
<div>
<ul>
{this.state.files.map((file, index) => {
return (
<li
key={index}
onClick={() => {
this.download(file.link, file.name);
}}
>
{file.name}
</li>
);
})}
</ul>
</div>
<output id="list" />
</div>
);
}
}
export default Download;