React PDF上传器问题:如何索引Div块关联对应PDF文件
解决React PDF上传器中View按钮总是显示最后一个文件的问题
嘿,我完全懂你现在的困扰!作为React新手,这种状态管理的小坑特别容易踩——咱们来一步步把它搞定。
问题根源
你现在的代码里,所有上传的PDF共享同一个fileDisplay、title和visible状态变量。不管点击哪个文件的View按钮,都会触发同一个模态框,而模态框显示的是最后一次更新的fileDisplay和title值,所以才会出现“点旧文件却显示新文件”的情况。
解决方案思路
核心是让每个上传的文件都保存自己的显示信息,而不是用全局的状态变量。我们需要:
- 把
uploadedFiles从单纯的文件名数组,改成包含文件名、文件预览URL的对象数组 - 用一个单独的状态变量记录当前要查看的文件(而不是全局控制模态框显示)
- 让每个View按钮点击时,把当前对应的文件设置为“要查看的文件”
修改后的完整代码
我把关键修改的地方用注释标出来了,你可以对照着看:
import { FilePicker } from 'react-file-picker'; import Button from 'react-bootstrap/Button'; import Modal from 'react-awesome-modal'; import '../styles/transcripts.css'; import { Component } from 'react'; // 别忘了导入Component! class Transcripts extends Component { constructor() { super(); this.state = { visible: false, uploadedFiles: [], currentViewingFile: null // 新增:记录当前要查看的文件对象 }; } openModal() { this.setState({ visible: true }); } closeModal() { this.setState({ visible: false, currentViewingFile: null }); // 关闭时清空当前文件 } handleFileChange = file => { // 检查文件是否已上传(现在要检查文件名) const isDuplicate = this.state.uploadedFiles.some(item => item.name === file.name); if (isDuplicate) { alert("Duplicate file. You've already uploaded this."); } else { // 创建文件预览URL,并把文件对象(包含name和url)添加到数组 const fileUrl = URL.createObjectURL(file); this.setState(prevState => ({ uploadedFiles: [...prevState.uploadedFiles, { name: file.name, url: fileUrl }] })); } }; delete(itemName) { if (window.confirm("Delete '" + itemName + "'?")) { // 删除时要找到对应的文件并revoke URL,避免内存泄漏 const fileToDelete = this.state.uploadedFiles.find(f => f.name === itemName); if (fileToDelete) { URL.revokeObjectURL(fileToDelete.url); } this.setState(prevState => ({ uploadedFiles: prevState.uploadedFiles.filter(f => f.name !== itemName) })); } } // 修改:接收当前文件作为参数,点击时设置currentViewingFile并打开模态框 handleViewFile(file) { this.setState({ currentViewingFile: file }); this.openModal(); } // 修改:模态框的内容依赖currentViewingFile renderModal() { const { currentViewingFile } = this.state; if (!currentViewingFile) return null; return ( <Modal visible={this.state.visible} min-width="800" min-height="200" effect="fadeInDown" onClickAway={() => this.closeModal()} > <div> <h1>{currentViewingFile.name}</h1> <embed src={currentViewingFile.url} width="800px" height="700px"/> <br /><br /><input type="text" /> </div> </Modal> ); } fileUploader() { return ( <div> <FilePicker extensions={["pdf"]} onChange={this.handleFileChange} onError={errMsg => alert("Invalid file type.\nMust be a .pdf file")} > <Button style={{ backgroundColor: "#f57505", width: "75px" }}> Upload </Button> </FilePicker> </div> ); } renderTranscripts() { if (this.state.uploadedFiles.length === 0) { return ( <div> <div id="rectanglePadding"> <div id="nothingToShow"> <h1>There are no transcripts to display! </h1> </div> </div> <div style={{ textAlign: "center" }}> <br /> <br /> {this.fileUploader()} </div> </div> ); } const noteItems = this.state.uploadedFiles.map(file => ( // 现在遍历的是文件对象 <span key={file.name}> {/* 别忘了加key!React列表需要唯一key */} <span id="blockStyling"> <span> {" "} <a id="textStyling">{file.name}</a>  </span> <p /> <p id="textStyling2"> Potential Issues: <br /> - Short by 2 math credits <br /> - Missing an elective </p> <br /> <div style={{ textAlign: "center" }}> {/* 修改:点击时调用handleViewFile并传入当前文件 */} <Button style={{ backgroundColor: "#f57505", width: "165px" }} onClick={() => this.handleViewFile(file)} > View </Button> <br /> <Button style={{ backgroundColor: "#f57505", width: "165px", }} onClick={() => this.delete(file.name)} > Delete </Button> <br /> <br /> </div> </span> <br /> <br /> <br /> </span> )); return ( <div> <br /> <h1 style={{ textDecoration: "underline", paddingLeft: "30px" }}> My Transcripts </h1> {noteItems} {/* 把模态框放在这里,只渲染一次 */} {this.renderModal()} <div style={{ textAlign: "center" }}> <br /> <br /> {this.fileUploader()} </div> </div> ); } render() { return <div>{this.renderTranscripts()}</div>; } // 组件卸载时,清理所有文件的URL,避免内存泄漏 componentWillUnmount() { this.state.uploadedFiles.forEach(file => { URL.revokeObjectURL(file.url); }); } } export default Transcripts;
关键修改点说明
- 状态结构优化:把
uploadedFiles改成对象数组,每个对象包含name(文件名)和url(预览URL),这样每个文件都有自己的独立信息。 - 当前查看文件状态:新增
currentViewingFile状态,专门记录用户点击的要查看的文件,模态框的内容完全依赖这个变量。 - View按钮逻辑:每个View按钮点击时,把对应的文件对象传给
handleViewFile,设置currentViewingFile后再打开模态框。 - 内存泄漏防护:在删除文件和组件卸载时,调用
URL.revokeObjectURL()释放文件URL占用的内存,这是很重要的细节哦! - 列表key:给遍历的列表项加上
key={file.name},符合React的列表渲染规范。
这样修改后,每个文件的View按钮都会触发对应文件的预览,再也不会串文件啦!
内容的提问来源于stack exchange,提问作者coder_coder123
相关产品推荐
相关产品推荐

