fix(月视图): 首屏农历不变红,切视图才生效 #6
用户反馈:刚打开应用时,月视图里有事件的日期农历没变红;
切到周视图再切回月视图,才变红。
## 根因:ForEach 的 key 不完整
月视图的 key 是:
(cell) => `${cell.dateMs}_${cell.dateMs === this.selectedDate}`
**没有包含 hasEvent。**
首屏时序:
1. aboutToAppear → rebuildPages(),此时 events 为空,hasEvent 全 false
2. reloadEvents() 拉回数据 → rebuildPages() 再次重建,hasEvent 变 true
3. 但每个格子的 key 完全没变(dateMs 和选中态都没变)
⇒ ArkUI 判定"这些项没有变化",**复用旧组件、跳过重绘**,颜色停在旧值
切视图能恢复,是因为切回来时整棵子树被重新创建,绕过了 diff。
## 修法
把影响渲染的**全部**状态纳入 key:hasEvent / isToday / 是否选中。
key 变了 → ArkUI 视为新项 → 重建该格子 → 颜色正确。
## 顺带加固
中间层"每周"的 key 原来是 `String(idx)`(0..5),跨月翻页时序号永远重复,
会让 ArkUI 误判为同一周而复用旧节点。改为用该周首日的 dateMs 做 key。
## 为什么不改成 @Observed / @ObjectLink
monthPages 是二维数组(MonthCell[][]),buildMonthCells 返回新数组、
chunkCells 又一次切片,改成可观察对象要动整条构建链路,
牵涉面大且容易引入其它 diff 问题。用 key 是最小、最稳的改法。
⚠️ 通用教训:ArkUI 的 ForEach 用自定义 key 时,**key 必须覆盖所有影响渲染的
字段**。漏一个字段,该字段更新后界面就不刷新 —— 而且这类 bug 只在
"同一项的数据变了、key 没变"时出现,首次渲染永远是正常的,很容易漏测。
This commit is contained in:
@@ -1706,7 +1706,8 @@ struct Index {
|
|||||||
.onClick(() => {
|
.onClick(() => {
|
||||||
this.selectedDate = cell.dateMs;
|
this.selectedDate = cell.dateMs;
|
||||||
})
|
})
|
||||||
}, (cell: MonthCell) => `${cell.dateMs}_${cell.dateMs === this.selectedDate}_${cell.hasEvent}`)
|
}, (cell: MonthCell) => `${cell.dateMs}_${cell.dateMs === this.selectedDate}`
|
||||||
|
+ `_he${cell.hasEvent ? 1 : 0}_td${cell.isToday ? 1 : 0}`)
|
||||||
}
|
}
|
||||||
.width('100%')
|
.width('100%')
|
||||||
.padding({ left: 12, right: 12 })
|
.padding({ left: 12, right: 12 })
|
||||||
@@ -1808,13 +1809,20 @@ struct Index {
|
|||||||
Column({ space: 2 }) {
|
Column({ space: 2 }) {
|
||||||
ForEach(this.chunkCells(cells), (week: MonthCell[], idx: number) => {
|
ForEach(this.chunkCells(cells), (week: MonthCell[], idx: number) => {
|
||||||
Row({ space: 2 }) {
|
Row({ space: 2 }) {
|
||||||
|
// ⭐ key 必须包含影响渲染的**全部**状态:hasEvent / isToday /
|
||||||
|
// 是否选中。只放 dateMs+选中态时,数据加载完成后 hasEvent 从
|
||||||
|
// false 变 true 但 key 没变 ⇒ ArkUI 复用旧组件、跳过重绘,
|
||||||
|
// 表现为"首屏农历不变红,切一次视图才正常"。
|
||||||
ForEach(week, (cell: MonthCell) => {
|
ForEach(week, (cell: MonthCell) => {
|
||||||
this.dayCell(cell)
|
this.dayCell(cell)
|
||||||
}, (cell: MonthCell) => `${cell.dateMs}_${cell.dateMs === this.selectedDate}`)
|
}, (cell: MonthCell) => `${cell.dateMs}_${cell.dateMs === this.selectedDate}`
|
||||||
|
+ `_he${cell.hasEvent ? 1 : 0}_td${cell.isToday ? 1 : 0}`)
|
||||||
}
|
}
|
||||||
.width('100%')
|
.width('100%')
|
||||||
.layoutWeight(this.wideLayout ? 1 : 0)
|
.layoutWeight(this.wideLayout ? 1 : 0)
|
||||||
}, (week: MonthCell[], idx: number) => String(idx))
|
// 用"这一周首日的 dateMs"当 key,而不是序号:跨月翻页时序号会重复,
|
||||||
|
// 会让 ArkUI 误以为是同一周而复用旧节点。
|
||||||
|
}, (week: MonthCell[], idx: number) => `wk${week.length > 0 ? week[0].dateMs : idx}`)
|
||||||
}
|
}
|
||||||
.width('100%')
|
.width('100%')
|
||||||
// 注意:不要给页面设百分比高度——竖屏时 Swiper 是自然高度,
|
// 注意:不要给页面设百分比高度——竖屏时 Swiper 是自然高度,
|
||||||
|
|||||||
Reference in New Issue
Block a user