forked from gxwebsoft/mp-10550
docs: 更新优惠券相关文档- 新增优惠券API集成文档
- 新增优惠券卡片对齐修复文档 - 新增优惠券状态显示调试文档 - 新增优惠券组件警告修复文档- 更新用ShopInfo Hook字段迁移文档 - 更新Arguments关键字修复文档
This commit is contained in:
254
docs/INFINITE_LOOP_FIX.md
Normal file
254
docs/INFINITE_LOOP_FIX.md
Normal file
@@ -0,0 +1,254 @@
|
||||
# 🚨 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工作正常,不再有无限循环问题!** 🚀
|
||||
Reference in New Issue
Block a user