Redux vs Jotai vs Zustand,在 RN 列表里的真实差异
本文对比了Redux、Jotai和Zustand三种状态管理方案在React Native列表性能优化中的表现。核心结论是:状态更新粒度直接影响列表渲染效率。Redux采用广播式更新,容易导致全局重渲染;Jotai通过原子订阅优化但需要合理设计;Zustand的选择器订阅模型最符合RN的显式渲染特性。作者建议根据状态类型分层使用不同方案:Redux管理全局数据,Zustand/Jotai处理交互状
@[toc]
如果你做过 RN 列表,一定经历过这种阶段:
- 刚开始:
useState挺顺 - 状态多了:开始抽 Redux
- 列表卡了:疯狂 memo / useCallback
- 还是卡:开始怀疑人生
问题真的在 FlatList 吗?
大多数时候,在状态模型。
统一测试场景
为了公平,我们先约定一个非常常见的场景:
- 一个商品列表(100 条)
- 每一项可以点赞
- 点赞状态会影响 UI
- 不考虑网络,只看本地状态更新
Redux:全局广播型
典型写法
function List() {
const likedMap = useSelector(state => state.likedMap)
return (
<FlatList
data={data}
renderItem={({ item }) => (
<Item
item={item}
liked={likedMap[item.id]}
/>
)}
/>
)
}
一次点赞发生了什么?
我们不讲概念,直接讲链路:
- dispatch
- reducer 返回新 likedMap
- useSelector 命中
- List 重新 render
- FlatList 重新计算 renderItem
- 所有 Item 重新走 props 对比
哪怕你:
- memo 了 Item
- useCallback 了 renderItem
List 这一层,永远逃不掉。
Redux 的本质问题
状态变化是“广播式”的,而列表最怕广播。
Redux 很适合:
- 页级数据
- 配置
- 权限
- 请求缓存
但它对“高频、局部、交互型状态”是天然不友好的。
Jotai:原子级订阅
我们换成 Jotai。
Atom 定义
const likedAtom = atom<Record<string, boolean>>({})
Item 组件直接订阅
function Item({ id }) {
const [likedMap, setLikedMap] = useAtom(likedAtom)
const liked = likedMap[id]
return (
<Pressable
onPress={() =>
setLikedMap(prev => ({
...prev,
[id]: !prev[id]
}))
}
>
<Text>{liked ? '❤️' : '🤍'}</Text>
</Pressable>
)
}
看起来好一点,但问题还在
为什么?
因为:
- likedMap 还是一个大对象
- atom 的 value 还是整体变化
- 所有订阅这个 atom 的组件都会更新
Jotai 没有魔法,它只是更细粒度。
正确的 Jotai 用法
关键在这一步:
const likedAtomFamily = atomFamily((id: string) =>
atom(false)
)
function Item({ id }) {
const [liked, setLiked] = useAtom(likedAtomFamily(id))
return (
<Pressable onPress={() => setLiked(v => !v)}>
<Text>{liked ? '❤️' : '🤍'}</Text>
</Pressable>
)
}
现在变化的是:
- 一个 atom
- 一个 item
- 一个订阅者
这时 Jotai 的优势才真正出现。
Zustand:选择器驱动型
Zustand 是 RN 圈里这两年非常受欢迎的状态库。
Store 定义
const useStore = create(set => ({
likedMap: {},
toggleLike: (id) =>
set(state => ({
likedMap: {
...state.likedMap,
[id]: !state.likedMap[id],
},
})),
}))
Item 级别订阅
function Item({ id }) {
const liked = useStore(
state => state.likedMap[id]
)
const toggleLike = useStore(
state => state.toggleLike
)
return (
<Pressable onPress={() => toggleLike(id)}>
<Text>{liked ? '❤️' : '🤍'}</Text>
</Pressable>
)
}
Zustand 在这里做对了什么?
重点只有一个:
selector 是订阅边界。
- likedMap 整体变没关系
- selector 只关心
likedMap[id] - 其他 item 不会被通知
这点和 Redux 完全不同。
三者在 RN 列表里的核心差异对比
| 维度 | Redux | Jotai | Zustand |
|---|---|---|---|
| 更新模型 | 广播 | 原子订阅 | 选择器订阅 |
| 默认粒度 | 全局 | 原子 | selector |
| 列表友好度 | 低 | 中(需设计) | 高 |
| 心智成本 | 中 | 高 | 低 |
| 易踩坑指数 | 高 | 中 | 低 |
为什么 Zustand 在 RN 圈更“顺手”
不是偶然。
RN 的渲染模型决定了:
- 谁订阅,谁重渲
- 渲染成本非常直观
- 没有浏览器兜底
Zustand 的 selector 模型,天然契合 RN 的这种“显式渲染”。
但 Zustand 也不是银弹
需要注意几个现实问题:
- store 过大会失控
- selector 写得不好一样重渲
- 很多人开始“什么都放 store”
所以记住一句话:
Zustand 是局部状态放大器,不是全局垃圾桶。
一个推荐的组合方案
在真实 RN 项目里,一个非常稳妥的搭配是:
- Redux:页面数据、接口缓存、权限
- Zustand / Jotai:列表交互、UI 状态
- useState:item 内部临时态
这不是“多此一举”,而是按渲染成本分层。
从状态库差异,反推 RN 的本质
你会发现一个很残酷的事实:
RN 不会帮你隐藏状态设计的错误。
Web 项目里还能靠浏览器苟住,
RN 会把每一次设计失误,直接变成卡顿反馈给你。
一句话总结
如果只记一句:
在 RN 列表里,谁能把“谁重渲”控制到最小,谁就赢了。
Redux 赢在秩序,
Jotai 赢在精细,
Zustand 赢在直觉。
更多推荐



所有评论(0)