如何在react-jsonschema-form的FileWidget处理文件时显隐加载动画
解决react-jsonschema-form FileWidget大文件加载时的动画提示问题
既然你没法修改FileWidget的源码,而且已经拿到了组件的ref,我们可以通过外部监听事件+状态控制的方式来实现加载动画的显示/隐藏,具体方案如下:
核心思路
FileWidget处理文件转Base64是异步过程(依赖FileReader API),我们需要抓住两个关键触发点:
- 用户选择文件时立即触发加载动画
- 文件处理完成(组件内部
filesInfo状态更新)时关闭加载动画
具体实现步骤
1. 在父组件中维护加载状态
首先在你的父组件里定义一个控制加载动画的状态:
import { useState, useEffect, useRef } from 'react'; function YourFormComponent() { const [isFileLoading, setIsFileLoading] = useState(false); const fileWidgetRef = useRef(null); // 这是你持有的FileWidget的ref // 后续逻辑写在这里... }
2. 拦截input选择事件,触发加载动画
我们可以给FileWidget内部的input元素添加额外的事件监听,当用户选择文件时立刻开启加载状态:
useEffect(() => { const inputElement = fileWidgetRef.current?.inputRef; if (!inputElement) return; // 保存组件原有的onChange方法,避免覆盖后丢失原有逻辑 const originalOnChange = inputElement.onchange; // 自定义的change事件处理函数 const handleFileSelect = (e) => { // 立即开启加载动画 setIsFileLoading(true); // 调用组件原有的文件处理逻辑 originalOnChange.call(inputElement, e); }; // 替换input的onchange事件 inputElement.onchange = handleFileSelect; // 组件卸载时恢复原事件,避免内存泄漏 return () => { inputElement.onchange = originalOnChange; }; }, []);
3. 监听处理完成事件,关闭加载动画
FileWidget处理完文件后会更新内部的filesInfo状态,我们可以通过两种方式监听这个完成信号:
方式一:监听组件内部状态变化(直接高效)
利用定时器轮询组件内部的filesInfo状态,当它从空变为有内容时,说明文件处理完成:
useEffect(() => { const widgetInstance = fileWidgetRef.current; if (!widgetInstance) return; const checkProcessingStatus = () => { const currentFiles = widgetInstance.state.filesInfo; // 如果filesInfo存在且有内容,说明处理完成 if (currentFiles && currentFiles.length > 0) { setIsFileLoading(false); } }; // 初始检查一次 checkProcessingStatus(); // 每100ms检查一次,平衡性能和响应速度 const interval = setInterval(checkProcessingStatus, 100); return () => clearInterval(interval); }, [fileWidgetRef.current]);
方式二:监听DOM变化(更稳定,无状态访问风险)
FileWidget会渲染FilesInfo组件展示已选文件,处理完成后这个DOM元素的内容会更新。我们可以用MutationObserver监听DOM变化:
useEffect(() => { const widgetInstance = fileWidgetRef.current; if (!widgetInstance) return; // 找到FilesInfo的DOM容器(根据组件渲染结构定位) const filesInfoContainer = widgetInstance.current?.children[1]; if (!filesInfoContainer) return; // 创建观察者实例,监听DOM子节点变化 const observer = new MutationObserver(() => { // DOM更新说明文件处理完成,关闭动画 setIsFileLoading(false); // 只监听一次,避免重复触发 observer.disconnect(); }); observer.observe(filesInfoContainer, { childList: true, subtree: true }); // 组件卸载时停止监听 return () => observer.disconnect(); }, [fileWidgetRef.current]);
4. 渲染加载动画
最后根据isFileLoading状态显示/隐藏加载动画,比如放在FileWidget附近:
return ( <div className="form-container"> {/* 你的react-jsonschema-form表单 */} <Form schema={yourSchema} widgets={{ file: FileWidget }} widgetRefs={{ file: fileWidgetRef }} // 传递ref给FileWidget /> {/* 加载动画 */} {isFileLoading && ( <div className="loading-spinner"> <span>正在处理文件,请稍候...</span> {/* 这里可以替换成你自己的加载图标组件 */} </div> )} </div> );
注意事项
- 多选文件场景下,
filesInfo会是一个数组,判断逻辑依然有效。 - 定时器间隔可根据实际情况调整,100ms既能保证响应及时,又不会过度消耗性能。
- MutationObserver方式更推荐,它不依赖组件内部的state结构,适配性更强。
内容的提问来源于stack exchange,提问作者Dónal
相关产品推荐
相关产品推荐

