Files
template-10584/docs/INFINITE_LOOP_FIX.md
赵忠林 1b24a611a8 docs: 更新优惠券相关文档- 新增优惠券API集成文档
- 新增优惠券卡片对齐修复文档
- 新增优惠券状态显示调试文档
- 新增优惠券组件警告修复文档- 更新用ShopInfo Hook字段迁移文档
- 更新Arguments关键字修复文档
2025-08-15 01:52:36 +08:00

255 lines
6.1 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.

# 🚨 useShopInfo 无限循环问题修复
## 问题描述
`useShopInfo` Hook 出现无限循环请求的问题,控制台不断输出 `shopInfo` 请求日志。
## 🔍 问题分析
### 根本原因
Hook中存在循环依赖导致的无限循环
```typescript
// 问题代码 ❌
const fetchShopInfo = useCallback(async (forceRefresh = false) => {
if (!forceRefresh && loadShopInfoFromStorage()) {
return shopInfo; // 依赖shopInfo
}
// ...
}, [shopInfo, loadShopInfoFromStorage, saveShopInfoToStorage]); // 依赖shopInfo
useEffect(() => {
fetchShopInfo(); // 依赖fetchShopInfo
}, [fetchShopInfo]); // 当fetchShopInfo变化时重新执行
```
### 循环链路
1. `useEffect` 依赖 `fetchShopInfo`
2. `fetchShopInfo` 依赖 `shopInfo`
3.`shopInfo` 更新时,`fetchShopInfo` 重新创建
4. `fetchShopInfo` 变化触发 `useEffect` 重新执行
5. `useEffect` 再次调用 `fetchShopInfo`
6. 无限循环 🔄
## 🔧 修复方案
### 1. **移除fetchShopInfo对shopInfo的依赖**
#### 修复前 ❌
```typescript
const fetchShopInfo = useCallback(async (forceRefresh = false) => {
// 如果不是强制刷新,先尝试从缓存加载
if (!forceRefresh && loadShopInfoFromStorage()) {
return shopInfo; // ❌ 依赖shopInfo导致循环
}
// ...
}, [shopInfo, loadShopInfoFromStorage, saveShopInfoToStorage]);
```
#### 修复后 ✅
```typescript
const fetchShopInfo = useCallback(async (forceRefresh = false) => {
try {
setLoading(true);
setError(null);
const data = await getShopInfo();
setShopInfo(data);
// 保存到本地存储
saveShopInfoToStorage(data);
return data;
} catch (error) {
// 错误处理...
} finally {
setLoading(false);
}
}, [saveShopInfoToStorage]); // ✅ 移除shopInfo依赖
```
### 2. **重构初始化逻辑**
#### 修复前 ❌
```typescript
useEffect(() => {
fetchShopInfo(); // ❌ 依赖fetchShopInfo导致循环
}, [fetchShopInfo]);
```
#### 修复后 ✅
```typescript
useEffect(() => {
const initShopInfo = async () => {
// 先尝试从缓存加载
const hasCache = loadShopInfoFromStorage();
// 如果没有缓存,则从服务器获取
if (!hasCache) {
await fetchShopInfo();
}
};
initShopInfo();
}, []); // ✅ 空依赖数组,只执行一次
```
### 3. **独立的刷新函数**
#### 修复前 ❌
```typescript
const refreshShopInfo = useCallback(() => {
return fetchShopInfo(true); // ❌ 依赖fetchShopInfo
}, [fetchShopInfo]);
```
#### 修复后 ✅
```typescript
const refreshShopInfo = useCallback(async () => {
try {
setLoading(true);
setError(null);
const data = await getShopInfo();
setShopInfo(data);
// 保存到本地存储
saveShopInfoToStorage(data);
return data;
} catch (error) {
// 错误处理...
} finally {
setLoading(false);
}
}, [saveShopInfoToStorage]); // ✅ 独立实现,避免循环依赖
```
## 📊 修复对比
| 项目 | 修复前 | 修复后 | 说明 |
|------|--------|--------|------|
| **fetchShopInfo依赖** | `[shopInfo, ...]` | `[saveShopInfoToStorage]` | 移除shopInfo依赖 |
| **useEffect依赖** | `[fetchShopInfo]` | `[]` | 只执行一次初始化 |
| **缓存检查** | 在fetchShopInfo中 | 在useEffect中 | 分离关注点 |
| **刷新函数** | 依赖fetchShopInfo | 独立实现 | 避免循环依赖 |
| **请求次数** | 无限循环 | 按需请求 | 性能优化 |
## ✅ 修复效果
### 修复前 ❌
```
🔄 无限循环请求
📊 控制台不断输出shopInfo日志
⚡ 性能问题,浪费网络资源
🐛 用户体验差,页面卡顿
```
### 修复后 ✅
```
✅ 只在需要时请求一次
📊 控制台日志正常
⚡ 性能优化,智能缓存
🚀 用户体验良好,页面流畅
```
## 🎯 Hook执行流程
### 修复后的正确流程
```
1. 组件挂载
2. useEffect执行只执行一次
3. 检查本地缓存
4. 如果有缓存 → 使用缓存数据,结束
5. 如果无缓存 → 调用fetchShopInfo
6. 获取数据,更新状态,保存缓存
7. 结束,不再重复请求
```
## 🧪 验证方法
### 1. **控制台检查**
- ✅ 不再有重复的shopInfo请求日志
- ✅ 只在初始化时请求一次
- ✅ 刷新时才会重新请求
### 2. **网络面板检查**
- ✅ Network面板中只有必要的请求
- ✅ 没有重复的/shop/getShopInfo请求
- ✅ 缓存机制正常工作
### 3. **功能验证**
- ✅ 商店信息正常显示
- ✅ Logo和网站名称正确
- ✅ 缓存机制工作正常
- ✅ 手动刷新功能正常
## 🛠️ 预防措施
### 1. **避免循环依赖**
```typescript
// ❌ 避免这样的依赖关系
const funcA = useCallback(() => {
// 使用stateB
}, [stateB]);
const funcB = useCallback(() => {
funcA();
}, [funcA]);
useEffect(() => {
funcB();
}, [funcB]);
```
### 2. **合理使用useCallback依赖**
```typescript
// ✅ 只依赖真正需要的值
const fetchData = useCallback(async () => {
// 不要在依赖数组中包含会变化的状态
}, [/* 只包含稳定的依赖 */]);
```
### 3. **useEffect依赖管理**
```typescript
// ✅ 初始化逻辑使用空依赖数组
useEffect(() => {
// 初始化逻辑
}, []); // 只执行一次
// ✅ 响应式逻辑明确依赖
useEffect(() => {
// 响应某个值的变化
}, [specificValue]);
```
## 📈 性能改进
### 请求优化
-**减少网络请求**:从无限循环到按需请求
-**智能缓存**30分钟缓存机制正常工作
-**内存优化**:避免不必要的重渲染
### 用户体验
-**页面流畅**:消除卡顿问题
-**快速加载**:缓存数据立即可用
-**错误处理**:网络失败时使用缓存
## 🎉 总结
通过重构Hook的依赖关系和执行流程成功修复了无限循环问题
-**移除循环依赖**fetchShopInfo不再依赖shopInfo
-**优化初始化**useEffect只执行一次
-**独立刷新函数**:避免函数间的循环依赖
-**保持功能完整**:所有原有功能正常工作
-**性能提升**:从无限请求到智能缓存
**现在useShopInfo Hook工作正常不再有无限循环问题** 🚀