TypeScript拖拽文件夹遍历后无法访问数组元素求助
拖拽文件夹遍历后无法访问数组元素的问题解决
问题场景
在React拖拽组件中实现了traverseDirectory函数用于递归遍历文件夹获取文件,但调用后出现以下异常:
- 打印数组对象时能看到有元素,但直接访问数组长度、具体元素时返回空或
undefined - 尝试用
useState更新数组或直接修改数组均无效
函数代码
export const traverseDirectory = ( directory: any, file_array:File[], path_array:string[] ) => { const reader = directory.createReader(); reader.readEntries((entries: any) => { entries.forEach((entry: any) => { if (entry.isFile) { path_array.push(entry.fullPath); entry.file((file: File) => { file_array.push(file); }); } else if (entry.isDirectory) { traverseDirectory(entry,file_array,path_array); } }); }); return { 'files': file_array, 'paths': path_array, } };
调用代码
const items = event.dataTransfer.items; const entry = item.webkitGetAsEntry(); if (entry) { // Recursively traverse the directory and collect files const data = traverseDirectory(entry, folderFiles, relativePaths); //1 console.log(data) //2 console.log(data.files); //3 console.log(data.files.length); //4 console.log(data.files[0]); }
问题根源
核心原因是异步操作未完成就返回了结果:
reader.readEntries是异步API,它的回调函数会在浏览器读取完目录条目后才执行entry.file同样是异步API,获取文件对象的回调也是延迟执行的
当traverseDirectory返回{files: file_array, paths: path_array}时,两个异步回调都还没执行,此时数组是空的。而浏览器的console会对引用类型做“懒更新”——打印的时候显示的是数组最终的状态,但代码执行到console.log(data.files.length)时,数组确实还是空的。
解决方案
将函数改造为基于Promise的异步函数,确保所有异步操作完成后再返回结果:
修改后的traverseDirectory函数
export const traverseDirectory = async (directory: any): Promise<{files: File[], paths: string[]}> => { const file_array: File[] = []; const path_array: string[] = []; // 封装readEntries为Promise const readEntries = (reader: any): Promise<any[]> => { return new Promise((resolve) => { reader.readEntries((entries: any[]) => { resolve(entries); }); }); }; // 封装entry.file为Promise const getFile = (entry: any): Promise<File> => { return new Promise((resolve) => { entry.file((file: File) => { resolve(file); }); }); }; const entries = await readEntries(directory.createReader()); for (const entry of entries) { if (entry.isFile) { path_array.push(entry.fullPath); const file = await getFile(entry); file_array.push(file); } else if (entry.isDirectory) { // 递归遍历子目录,合并结果 const subDirResult = await traverseDirectory(entry); file_array.push(...subDirResult.files); path_array.push(...subDirResult.paths); } } return { files: file_array, paths: path_array }; };
修改后的调用代码(React中)
// 假设在事件回调中,比如onDrop const handleDrop = async (event: React.DragEvent) => { event.preventDefault(); const items = event.dataTransfer.items; for (const item of items) { const entry = item.webkitGetAsEntry(); if (entry && entry.isDirectory) { const data = await traverseDirectory(entry); // 此时可以正常访问数组元素 console.log(data.files.length); console.log(data.files[0]); // 如果需要更新组件状态 // setFolderFiles(data.files); // setRelativePaths(data.paths); } } };
关键说明
- 用Promise封装所有异步API,确保递归和文件读取操作按顺序完成
- 使用
async/await让异步代码看起来像同步代码,避免回调地狱 - React中更新状态时,要在异步操作完成后调用
setState,确保状态更新基于最新的数组结果
内容的提问来源于stack exchange,提问作者TSS Predator
相关产品推荐
相关产品推荐

