React中如何在Axios的多个.then()链中传递非Promise变量?
解决React中Axios下载文件时文件名传递问题
问题原因
React的useState更新是异步的,调用setFileName和setExtension后,状态不会立即改变,而是要等到下一次组件渲染才会更新。所以在链式调用的下一个.then()里,你拿到的还是旧的空字符串状态。
解决方案
方案1:在链式调用中直接传递文件名变量
不需要依赖state来传递文件名,直接在第一个.then()里把解析好的文件名、扩展名和blob数据一起返回,下一个.then()接收这些值即可:
const [fileName, setFileName] = useState("") const [extension, setExtension] = useState("") const downloadFile = useCallback(async (path) => { Axios.get( `http://localhost:8080/api${path}`, { responseType: 'blob' }) .then(res => { let headers = res.headers['content-disposition'] let parsedFileName = headers.split('filename=')[1].split('.')[0] let parsedExtension = headers.split('.')[1].split(';')[0] setFileName(parsedFileName) setExtension(parsedExtension) // 返回包含blob和文件名信息的对象 return { blob: res.data, fileName: parsedFileName, extension: parsedExtension } }) .then(({ blob, fileName, extension }) => { const url = window.URL.createObjectURL(blob) const link = document.createElement('a') const fullFileName = `${fileName}.${extension}` link.href = url link.download = fullFileName; document.body.appendChild(link) link.click() link.remove() URL.revokeObjectURL(url) }) .catch(err => console.log(err)) }, [])
方案2:合并两个.then()逻辑
既然文件名解析和下载操作是连续的,直接把它们放到同一个.then()里,避免链式传递的问题:
const [fileName, setFileName] = useState("") const [extension, setExtension] = useState("") const downloadFile = useCallback(async (path) => { Axios.get( `http://localhost:8080/api${path}`, { responseType: 'blob' }) .then(res => { let headers = res.headers['content-disposition'] let parsedFileName = headers.split('filename=')[1].split('.')[0] let parsedExtension = headers.split('.')[1].split(';')[0] setFileName(parsedFileName) setExtension(parsedExtension) // 直接在这里处理下载逻辑 const blob = res.data const url = window.URL.createObjectURL(blob) const link = document.createElement('a') const fullFileName = `${parsedFileName}.${parsedExtension}` link.href = url link.download = fullFileName; document.body.appendChild(link) link.click() link.remove() URL.revokeObjectURL(url) }) .catch(err => console.log(err)) }, [])
方案3:(不推荐)用useEffect监听状态变化
如果一定要依赖state来触发下载(比如需要根据状态变化做其他操作),可以用useEffect监听fileName和extension的变化,但这种方式会多一次渲染,不如直接传递变量高效:
const [fileName, setFileName] = useState("") const [extension, setExtension] = useState("") const [downloadBlob, setDownloadBlob] = useState(null) // 监听状态变化触发下载 useEffect(() => { if (downloadBlob && fileName && extension) { const url = window.URL.createObjectURL(downloadBlob) const link = document.createElement('a') const fullFileName = `${fileName}.${extension}` link.href = url link.download = fullFileName; document.body.appendChild(link) link.click() link.remove() URL.revokeObjectURL(url) // 下载完成后清空blob setDownloadBlob(null) } }, [downloadBlob, fileName, extension]) const downloadFile = useCallback(async (path) => { Axios.get( `http://localhost:8080/api${path}`, { responseType: 'blob' }) .then(res => { let headers = res.headers['content-disposition'] let parsedFileName = headers.split('filename=')[1].split('.')[0] let parsedExtension = headers.split('.')[1].split(';')[0] setFileName(parsedFileName) setExtension(parsedExtension) setDownloadBlob(res.data) }) .catch(err => console.log(err)) }, [])
内容的提问来源于stack exchange,提问作者user28751094
相关产品推荐
相关产品推荐

