7.0 KiB
7.0 KiB
TVAI Hooks 错误修复和文件选择功能改进总结
🐛 问题诊断
React Hooks 错误
Uncaught Error: Rendered fewer hooks than expected.
This may be caused by an accidental early return statement.
根本原因: 在React组件中,Hooks必须在每次渲染时以相同的顺序调用。如果在早期返回语句之后调用Hooks,会导致Hooks数量不一致的错误。
问题代码结构
export function TvaiExample() {
// ✅ 正确:Hooks在早期返回之前
const [state1] = useState();
const [state2] = useState();
// ❌ 错误:早期返回
if (loading) {
return <div>Loading...</div>;
}
// ❌ 错误:Hooks在早期返回之后
const callback = useCallback(() => {}, []);
}
✅ 修复方案
1. Hooks 重新排序
将所有Hooks调用移到早期返回语句之前:
export function TvaiExample() {
// ✅ 所有Hooks必须在早期返回之前
const { systemInfo, isLoading: systemLoading, initializeTvai } = useTvaiSystemInfo();
const { tasks, isLoading: tasksLoading, cancelTask, cleanupCompletedTasks } = useTvai();
// 状态Hooks
const [processingType, setProcessingType] = useState<'video' | 'image' | 'interpolation'>('video');
const [showAdvanced, setShowAdvanced] = useState(false);
const [inputPath, setInputPath] = useState('');
const [outputPath, setOutputPath] = useState('');
const [scaleFactor, setScaleFactor] = useState(2.0);
const [selectedPreset, setSelectedPreset] = useState('GENERAL');
const [selectedModel, setSelectedModel] = useState<UpscaleModel>('iris-3');
const [compression, setCompression] = useState(0.0);
const [blend, setBlend] = useState(0.0);
const [interpolationMultiplier, setInterpolationMultiplier] = useState(2.0);
// useCallback Hooks
const handleSelectInputFile = useCallback(async () => { /* ... */ }, [processingType]);
const handleSelectOutputFile = useCallback(async () => { /* ... */ }, [processingType]);
// 普通函数
const applyPreset = (presetName: string) => { /* ... */ };
const handleQuickProcess = async () => { /* ... */ };
// ✅ 早期返回在所有Hooks之后
if (systemLoading) {
return <div>Loading...</div>;
}
// 正常渲染
return <div>...</div>;
}
2. 清理未使用的变量
移除了以下未使用的状态变量:
currentStep和setCurrentStepqualityPreset和setQualityPresetoutputFormat和setOutputFormattargetFps和setTargetFpsisDragging和setIsDragging
3. 移除重复的函数定义
删除了重复定义的函数:
- 重复的
applyPreset函数 - 重复的
handleQuickProcess函数 - 重复的早期返回语句
🎯 文件选择功能改进
参考其他页面的实现
通过分析 ImageEditingTool.tsx 和其他工具页面,采用了统一的文件选择模式:
import { open } from '@tauri-apps/plugin-dialog';
const handleSelectFile = useCallback(async () => {
try {
const selected = await open({
multiple: false,
filters: [{ name: '视频文件', extensions: ['mp4', 'avi', 'mov'] }],
title: '选择视频文件',
});
if (selected && typeof selected === 'string') {
setInputPath(selected);
// 自动生成输出路径
generateOutputPath(selected);
}
} catch (error) {
console.error('选择文件失败:', error);
alert('选择文件失败: ' + error.message);
}
}, [processingType]);
文件格式配置
const FILE_FORMATS = {
video: {
name: '视频文件',
extensions: ['mp4', 'avi', 'mov', 'mkv', 'wmv', 'flv', 'm4v', 'webm', '3gp', 'ts']
},
image: {
name: '图片文件',
extensions: ['jpg', 'jpeg', 'png', 'bmp', 'tiff', 'webp', 'gif', 'svg']
}
};
UI 改进
从简单的文本输入框改为专业的文件选择按钮:
原来的设计:
<input
type="text"
value={inputPath}
onChange={(e) => setInputPath(e.target.value)}
placeholder="选择视频文件..."
/>
新的设计:
<button
onClick={handleSelectInputFile}
className="w-full flex items-center gap-3 px-4 py-3 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors text-left"
>
<FolderOpen className="w-5 h-5 text-gray-400 flex-shrink-0" />
<div className="flex-1 min-w-0">
{inputPath ? (
<div>
<div className="text-sm font-medium text-gray-900 truncate">
{inputPath.split('/').pop() || inputPath.split('\\').pop()}
</div>
<div className="text-xs text-gray-500 truncate">
{inputPath}
</div>
</div>
) : (
<span className="text-gray-500">
选择{processingType === 'image' ? '图片' : '视频'}文件...
</span>
)}
</div>
</button>
智能功能
- 自动路径生成: 根据输入文件自动生成输出路径
- 动态文件类型: 根据处理类型显示对应的文件格式
- 文件名显示: 显示文件名和完整路径
- 一键生成: 提供"自动生成输出路径"按钮
🔧 技术细节
Hooks 调用顺序规则
- 自定义Hooks:
useTvaiSystemInfo(),useTvai() - 状态Hooks:
useState() - 效果Hooks:
useCallback(),useMemo(),useEffect() - 早期返回: 在所有Hooks之后
- 事件处理函数: 普通函数,不是Hooks
文件选择最佳实践
- 统一API: 使用
@tauri-apps/plugin-dialog的open()函数 - 类型安全: TypeScript 类型检查
- 错误处理: try-catch 包装,用户友好的错误提示
- 用户体验: 加载状态、进度反馈、自动完成
性能优化
- useCallback: 防止不必要的重新渲染
- 依赖数组: 正确设置依赖项
- 条件渲染: 避免不必要的计算
📊 修复前后对比
| 方面 | 修复前 | 修复后 |
|---|---|---|
| Hooks错误 | ❌ 运行时崩溃 | ✅ 正常运行 |
| 文件选择 | ❌ 手动输入路径 | ✅ 原生文件对话框 |
| 用户体验 | ❌ 复杂操作 | ✅ 一键选择 |
| 错误处理 | ❌ 基础提示 | ✅ 详细错误信息 |
| 路径生成 | ❌ 手动输入 | ✅ 自动生成 |
| 文件格式 | ❌ 无限制 | ✅ 类型过滤 |
🚀 用户体验提升
操作流程简化
- 选择处理类型 → 自动设置文件格式过滤
- 点击选择文件 → 打开原生文件对话框
- 选择文件 → 自动生成输出路径
- 开始处理 → 一键启动
错误预防
- 文件格式验证: 只允许选择支持的格式
- 路径验证: 确保文件存在且可读
- 权限检查: 确保输出路径可写
- 友好提示: 清晰的错误信息和解决建议
总结
通过修复React Hooks的调用顺序问题和改进文件选择功能,TVAI工具现在具有:
✅ 稳定性 - 无运行时错误,符合React规范 ✅ 易用性 - 原生文件对话框,操作简单 ✅ 智能化 - 自动路径生成,类型过滤 ✅ 专业性 - 统一的UI设计,完善的错误处理
这些改进大大提升了用户体验,使TVAI工具更加稳定和易用。