React父组件调用文件上传子组件时如何获取返回文件名并更新state
解决方案
你需要做3处核心修改即可实现需求:
1. 修正父组件逻辑
首先修改父组件的回调函数,适配直接接收文件名参数,同时修正模板渲染的语法错误:
// ParentComponent.jsx import React, { Component } from 'react' // 注意导入名称要和子组件导出的名称对应,你之前子组件导出的是UploadFiles,这里保持一致即可 import UploadFiles from "../Services/FilesUpload"; class ParentComponent extends Component { constructor(props) { super(props) this.state = { fileName: null } } // 修改回调:直接接收文件名参数,不需要读取event.target.value changefileNameHandler = (fileName) => { this.setState({fileName: fileName}); } render() { return ( <div> {/* 修正渲染语法,要加花括号才能读取state值 */} <td>{this.state.fileName}</td> <UploadFiles onUpdate={this.changefileNameHandler}/> </div> ) } } export default ParentComponent
2. 修正子组件逻辑
首先接收父组件传入的onUpdate回调,在文件上传成功的位置触发回调传入文件名即可:
// FilesUpload.js import React, { useState, useEffect, useRef } from "react"; import UploadService from "../Services/FileUploadService"; import { ToastContainer, toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; // 接收父组件传入的onUpdate参数 const UploadFiles = ({ onUpdate }) => { const [selectedFiles, setSelectedFiles] = useState(undefined); const [progressInfos, setProgressInfos] = useState({ val: [] }); const [message, setMessage] = useState([]); const [fileInfos, setFileInfos] = useState([]); const progressInfosRef = useRef(null) useEffect(() => { UploadService.getFiles().then((response) => { setFileInfos(response.data); }); }, []); const selectFiles = (event) => { setSelectedFiles(event.target.files); setProgressInfos({ val: [] }); }; const upload = (idx, file) => { let _progressInfos = [...progressInfosRef.current.val]; return UploadService.upload(file, (event) => { _progressInfos[idx].percentage = Math.round( (100 * event.loaded) / event.total ); setProgressInfos({ val: _progressInfos }); }) .then(() => { toast.info(file.name + " Uploaded") // 上传成功后触发父组件回调,传入当前文件的名称 onUpdate?.(file.name) setMessage((prevMessage) => ([ ...prevMessage, "Uploaded the file successfully: " + file.name, ])); }) .catch(() => { _progressInfos[idx].percentage = 0; setProgressInfos({ val: _progressInfos }); setMessage((prevMessage) => ([ ...prevMessage, "Could not upload the file: " + file.name, ])); }); }; const uploadFiles = () => { const files = Array.from(selectedFiles); let _progressInfos = files.map(file => ({ percentage: 0, fileName: file.name })); progressInfosRef.current = { val: _progressInfos, } const uploadPromises = files.map((file, i) => upload(i, file)); Promise.all(uploadPromises) .then(() => UploadService.getFiles()) .then((files) => { setFileInfos(files.data); }); setMessage([]); }; return ( <div> {progressInfos && progressInfos.val.length > 0 && progressInfos.val.map((progressInfo, index) => ( <div className="mb-2" key={index}> <span>{progressInfo.fileName}</span> <div className="progress"> <div className="progress-bar progress-bar-info" role="progressbar" aria-valuenow={progressInfo.percentage} aria-valuemin="0" aria-valuemax="100" style={{ width: progressInfo.percentage + "%" }} > {progressInfo.percentage}% </div> </div> </div> ))} <div className="row my-3"> <div className="col-8"> <label className="btn btn-default p-0"> <input type="file" multiple onChange={selectFiles} /> </label> </div> <div className="col-4"> <button className="btn btn-success btn-sm" disabled={!selectedFiles} onClick={uploadFiles} > Upload </button> </div> </div> {message.length > 0 && ( <div className="alert alert-secondary" role="alert"> <ul> {message.map((item, i) => { return <li key={i}>{item}</li>; })} </ul> </div> )} <div className="card"> <ul className="list-group list-group-flush"> {/* 修正判断逻辑,原来的!fileInfos会导致有数据时不渲染 */} {fileInfos && fileInfos.map((file, index) => ( <li className="list-group-item" key={index}> </li> ))} </ul> </div> <ToastContainer position="top-center" autoClose={1000}/> </div> ); }; export default UploadFiles;
多文件上传适配说明
如果你的业务是多文件上传,需要存储所有上传文件的名称,可以把父组件的state改成数组类型,子组件所有文件上传完成后一次性传入文件名数组即可。
内容的提问来源于stack exchange,提问作者HighwayRob
相关产品推荐
相关产品推荐

