跳到主要内容

游牧周记第43期

· 阅读需 5 分钟
Suhe
This site owner

日常

阳台种植

新买了一些东西:

  • 2颗来自德宏的树莓苗,一颗黑色一颗红色,但只是苗,我分不出,分别用两个盆栽上,看来品质还行。
  • 一带(10斤)云南森林腐叶土,纯黑色,希望可以弥补有些植物酸性不足和肥少的问题(以前买的营养土似乎太寡淡了...)。
  • 苦楝油和皂液,混装加水后用,但最近似乎没发现红蜘蛛和虫害了。

Diablo IV s10

最近这个游戏打得有点频繁了,s9一直玩亡灵,但没啥突破,感觉冲层无力,最大的成就是最后几天得到一个4星的堕狱头盔,但赛季也结束了。 9月24日,s10上线,当天又以熟悉的亡灵试玩,一口气冲到55。现在大家都说新赛季德鲁伊最好玩,于是第二天改小德,2天打到凌晨,60(153)级,早中期的拍拍装配齐,感觉攻击力确实可以,但会暴毙,最大的问题是我们玩惯了纯招单手逛街的人,觉得有点腰酸背痛了,等凑齐装备,下次改渡鸦流派,看会否轻松点。

出行

高铁去老挝计划

原计划国庆假期后触发,后来又有点急想再提早两天。 办签证有2个方法,在网上电子签或到昆明的领事馆办。

领事馆据说最近热门到要排队,要实现拍摄合适的2寸白底照片,带上身份证等一堆证件,估计300多块钱/人手续费。 那是不是电子签好点呢?我开始也这么想,于是上了https://laoevisa.gov.la,填写了一些内容,上传以前的证件照和护照照片,每人收费36usd(看似便宜很多),正要提交,突然想起一个问题:如果填写有误,可以修改吗,会不会退钱?没地方问就问了GPT,结果是否的,听说照片要求6个月以内拍的,一些文字细节也不能出错,我真的担心可能过不了,那就打水飘了。 考虑良久,还是下楼找个小店拍照了,多年前去泰国也是这家拍的签证照。 原计划第二天就去领事馆,后来突然想改晚点去(比如11月),主要还是因为今年出游较多,有点倦怠了,琅勃拉邦一个地方对我的吸引力有限(真有趣的反而是高铁),旅行目标还不明确,就再等等吧。

开发

Unistyles的theme切换太懒了怎么办

上周解决了整体theme切换的问题,这周发现复杂点的app,部分页面和组件不更新,特别是tabs中和list中,ChatGPT给了个直接又简单的方案。如下:

问题其实是 react-native-unistyles v3+ 在 Expo SDK 54 里切换主题时,部分组件没有重新刷新。
这是因为 UnistylesRuntime.setTheme() 并不会强制触发整个 React 树的 re-render,尤其是被缓存的 screen / VirtualizedList item。


1. 为什么会发生

  • Tabs 场景:React Navigation 默认会缓存 tab screen,不会因为 theme 改变而自动 unmount/remount。

  • FlatList 场景:子项渲染由 renderItem 控制,如果 item 的 key 和 props 没变,React 不会 re-render。

  • UnistylesRuntime 的限制:调用 setTheme 实际只是更新内部 runtime theme,只有订阅 hook(useStyles, useTheme)的地方才会触发 re-render。


2. 常见坑

  1. 你用了 UnistylesRuntime.setAdaptiveThemes(true/false),但如果之前的组件挂载时拿到的 theme 是“老的”,它不会自动更新。

  2. React Navigation tab 屏幕缓存(默认 lazyunmountOnBlur: false),不会重新构建。

  3. FlatList 的 item 如果不依赖 theme 作为 extraData,不会刷新。


3. 解决方案

✅ 方法 A:确保 theme 改变能触发 re-render

const theme = useTheme(); // Unistyles hook

<FlatList
data={data}
renderItem={renderItem}
extraData={theme.name} // 强制当 theme.name 变化时刷新
/>

✅ 方法 B:给 Navigation 强制刷新

React Navigation 可以利用 key 来强制重新挂载:

<Tab.Navigator
key={theme.name} // 当 theme 变化时,强制整个 Tab.Navigator 重建
>
...
</Tab.Navigator>

或者在 _layout.tsx 根节点:

const theme = useTheme();

return (
<Stack key={theme.name}>
{/* theme 改变时整个 Stack 重新渲染 */}
</Stack>
);

✅ 方法 C:避免滥用 setAdaptiveThemes

  • 如果只想手动切换主题,用 UnistylesRuntime.setTheme(...) 就够了,不需要先关再开 adaptive。

  • adaptive 主要给 系统 theme 跟随 用。


4. 推荐写法(_layout.tsx)

useEffect(() => {
devLog('[Root Layout] new theme setting:', settingTheme)

if (settingTheme === 'system') {
UnistylesRuntime.setAdaptiveThemes(true)
} else {
UnistylesRuntime.setAdaptiveThemes(false)
UnistylesRuntime.setTheme(settingTheme as keyof UnistylesThemes)
}
}, [settingTheme])

然后在 Navigation 根加:

const theme = useTheme();

return (
<Stack key={theme.name}>
{/* screens */}
</Stack>
)

👉 总结:

  • FlatList 要用 extraData={theme}

  • Navigation 要用 key={theme.name} 来强制刷新缓存的 screen。

  • 不要频繁调用 setAdaptiveThemes,只在系统跟随模式时启用。


巧妙使用key参数,高! 控制不用太细,在根_layout.tsx或相应的总体导航组件加入即可。 我个人在(tabs)_layout.tsx加入。

skia组件在expo SDK 54后无法响应

升级expo到SDK 54后,发现skia绘制的组件,在外包裹上pressable或touchable,均无法响应点按。ChatGPT没有找到佐证材料,自己编了些理由,但居然得到了正确的方法。

它这样说的:

这个问题其实不是你代码本身的锅,而是 **Expo SDK 54 升级后 react-native-skia的事件穿透机制变了**。简单说就是: Skia 画布默认会「吃掉」触摸事件,导致外层的Pressable/Touchable 都接收不到点击。

✅ 解决方案

1. 禁用 Canvas 的触摸拦截

Canvas 加上:

<Canvas style={{ flex: 1 }} pointerEvents="none"> {/* 绘制 */} </Canvas>

这样 Canvas 就不会「吃掉」点击事件,父级 Pressable 就能响应。

于是就ok了。