227 lines
7.0 KiB
Markdown
227 lines
7.0 KiB
Markdown
# 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工具更加稳定和易用。
|