mixvideo-v2/TVAI_HOOKS_FIX_AND_FILE_SEL...

227 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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数量不一致的错误。
### 问题代码结构
```typescript
export function TvaiExample() {
// ✅ 正确Hooks在早期返回之前
const [state1] = useState();
const [state2] = useState();
// ❌ 错误:早期返回
if (loading) {
return <div>Loading...</div>;
}
// ❌ 错误Hooks在早期返回之后
const callback = useCallback(() => {}, []);
}
```
## ✅ 修复方案
### 1. Hooks 重新排序
将所有Hooks调用移到早期返回语句之前
```typescript
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``setCurrentStep`
- `qualityPreset``setQualityPreset`
- `outputFormat``setOutputFormat`
- `targetFps``setTargetFps`
- `isDragging``setIsDragging`
### 3. 移除重复的函数定义
删除了重复定义的函数:
- 重复的 `applyPreset` 函数
- 重复的 `handleQuickProcess` 函数
- 重复的早期返回语句
## 🎯 文件选择功能改进
### 参考其他页面的实现
通过分析 `ImageEditingTool.tsx` 和其他工具页面,采用了统一的文件选择模式:
```typescript
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]);
```
### 文件格式配置
```typescript
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 改进
从简单的文本输入框改为专业的文件选择按钮:
**原来的设计**:
```jsx
<input
type="text"
value={inputPath}
onChange={(e) => setInputPath(e.target.value)}
placeholder="选择视频文件..."
/>
```
**新的设计**:
```jsx
<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>
```
### 智能功能
1. **自动路径生成**: 根据输入文件自动生成输出路径
2. **动态文件类型**: 根据处理类型显示对应的文件格式
3. **文件名显示**: 显示文件名和完整路径
4. **一键生成**: 提供"自动生成输出路径"按钮
## 🔧 技术细节
### Hooks 调用顺序规则
1. **自定义Hooks**: `useTvaiSystemInfo()`, `useTvai()`
2. **状态Hooks**: `useState()`
3. **效果Hooks**: `useCallback()`, `useMemo()`, `useEffect()`
4. **早期返回**: 在所有Hooks之后
5. **事件处理函数**: 普通函数不是Hooks
### 文件选择最佳实践
1. **统一API**: 使用 `@tauri-apps/plugin-dialog``open()` 函数
2. **类型安全**: TypeScript 类型检查
3. **错误处理**: try-catch 包装,用户友好的错误提示
4. **用户体验**: 加载状态、进度反馈、自动完成
### 性能优化
1. **useCallback**: 防止不必要的重新渲染
2. **依赖数组**: 正确设置依赖项
3. **条件渲染**: 避免不必要的计算
## 📊 修复前后对比
| 方面 | 修复前 | 修复后 |
|------|--------|--------|
| Hooks错误 | ❌ 运行时崩溃 | ✅ 正常运行 |
| 文件选择 | ❌ 手动输入路径 | ✅ 原生文件对话框 |
| 用户体验 | ❌ 复杂操作 | ✅ 一键选择 |
| 错误处理 | ❌ 基础提示 | ✅ 详细错误信息 |
| 路径生成 | ❌ 手动输入 | ✅ 自动生成 |
| 文件格式 | ❌ 无限制 | ✅ 类型过滤 |
## 🚀 用户体验提升
### 操作流程简化
1. **选择处理类型** → 自动设置文件格式过滤
2. **点击选择文件** → 打开原生文件对话框
3. **选择文件** → 自动生成输出路径
4. **开始处理** → 一键启动
### 错误预防
1. **文件格式验证**: 只允许选择支持的格式
2. **路径验证**: 确保文件存在且可读
3. **权限检查**: 确保输出路径可写
4. **友好提示**: 清晰的错误信息和解决建议
## 总结
通过修复React Hooks的调用顺序问题和改进文件选择功能TVAI工具现在具有
**稳定性** - 无运行时错误符合React规范
**易用性** - 原生文件对话框,操作简单
**智能化** - 自动路径生成,类型过滤
**专业性** - 统一的UI设计完善的错误处理
这些改进大大提升了用户体验使TVAI工具更加稳定和易用。