fix(NotificationContainer): 修复 React Hooks 调用顺序错误

**问题描述**
React 检测到 NotificationItem 组件中 Hooks 调用顺序不一致:

```
Warning: React has detected a change in the order of Hooks called by null.
Previous render: 24. useCallback
Next render:     25. useContext
```

**根本原因**
在第 433 行,`useColorModeValue` Hook 在条件对象展开中被调用:

```javascript
{...(isNewest && {
    borderTopColor: useColorModeValue(...),  //  违反 Hooks 规则
})}
```

当 `isNewest` 值变化时:
- `isNewest = false` → Hook 不调用
- `isNewest = true` → Hook 调用
- 导致不同渲染的 Hooks 数量不一致

**React Hooks 规则**
> Hooks 必须在组件顶层调用,不能在条件语句、循环或嵌套函数中调用

**修复内容**

1. **将 Hook 移到组件顶层** (第 349-353 行)
```javascript
// 最新通知的 borderTopColor(避免在条件语句中调用 Hook)
const newestBorderTopColor = useColorModeValue(
    `${typeConfig.colorScheme}.100`,
    `${typeConfig.colorScheme}.700`
);
```

2. **添加到 colors 对象** (第 365 行)
```javascript
const colors = useMemo(() => ({
    // ... 其他颜色
    newestBorderTop: newestBorderTopColor,
}), [/* dependencies */]);
```

3. **在 JSX 中使用预计算的值** (第 439 行)
```diff
  {...(isNewest && {
-     borderTopColor: useColorModeValue(...),
+     borderTopColor: colors.newestBorderTop,
  })}
```

**修复验证**
-  所有 Hooks 在每次渲染都以相同顺序调用
-  消除 React Hooks 警告
-  功能保持不变(视觉效果一致)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-10-30 18:43:19 +08:00
parent 5236976307
commit 0364b3a927

View File

@@ -346,6 +346,11 @@ const NotificationItem = React.memo(({ notification, onClose, isNewest = false }
`${typeConfig.colorScheme}.200`, `${typeConfig.colorScheme}.200`,
`${typeConfig.colorScheme}.700` `${typeConfig.colorScheme}.700`
); );
// 最新通知的 borderTopColor避免在条件语句中调用 Hook
const newestBorderTopColor = useColorModeValue(
`${typeConfig.colorScheme}.100`,
`${typeConfig.colorScheme}.700`
);
// 使用 useMemo 缓存颜色对象(避免不必要的重新创建) // 使用 useMemo 缓存颜色对象(避免不必要的重新创建)
const colors = useMemo(() => ({ const colors = useMemo(() => ({
@@ -357,7 +362,8 @@ const NotificationItem = React.memo(({ notification, onClose, isNewest = false }
metaText: metaTextColor, metaText: metaTextColor,
hoverBg: hoverBgColor, hoverBg: hoverBgColor,
closeButtonHoverBg: closeButtonHoverBgColor, closeButtonHoverBg: closeButtonHoverBgColor,
}), [priorityBgColor, borderColor, iconColor, textColor, subTextColor, metaTextColor, hoverBgColor, closeButtonHoverBgColor]); newestBorderTop: newestBorderTopColor,
}), [priorityBgColor, borderColor, iconColor, textColor, subTextColor, metaTextColor, hoverBgColor, closeButtonHoverBgColor, newestBorderTopColor]);
// 点击处理(只有真正可点击时才执行)- 使用 useCallback 优化 // 点击处理(只有真正可点击时才执行)- 使用 useCallback 优化
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
@@ -430,7 +436,7 @@ const NotificationItem = React.memo(({ notification, onClose, isNewest = false }
borderRight: '1px solid', borderRight: '1px solid',
borderRightColor: colors.border, borderRightColor: colors.border,
borderTop: '1px solid', borderTop: '1px solid',
borderTopColor: useColorModeValue(`${typeConfig.colorScheme}.100`, `${typeConfig.colorScheme}.700`), borderTopColor: colors.newestBorderTop,
})} })}
> >
{/* 头部区域:标题 + 可选标识 */} {/* 头部区域:标题 + 可选标识 */}