0%

前端状态管理新趋势——Zustand vs Jotai vs Recoil对比

在React应用开发中,状态管理一直是核心难题。Zustand、Jotai和Recoil作为新兴的状态管理方案,各自带来了独特的设计理念和解决方案。

介绍

  随着React应用规模的不断增长,传统的Context API和Redux等状态管理方案逐渐暴露出复杂性和冗余代码的问题。Zustand、Jotai和Recoil作为新一代状态管理库,通过更简洁的API和更轻量的体积,为开发者提供了新的选择。本文将深入分析这三种状态管理方案的特点、使用方法和适用场景。

Zustand

简介

Zustand是由Poimandres开发团队推出的状态管理库,其设计理念是提供一个极简、快速和可扩展的状态管理解决方案。

  • 特点
    • 零依赖,体积小巧(约1KB)
    • 不需要Provider包装
    • 支持中间件
    • 支持SSR
    • TypeScript友好

安装与基本使用

1
npm install zustand
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// stores/useUserStore.js
import { create } from 'zustand';

const useUserStore = create((set, get) => ({
// 状态
user: null,
isLoggedIn: false,
favorites: [],

// 操作方法
login: (userData) => set({ user: userData, isLoggedIn: true }),

logout: () => set({ user: null, isLoggedIn: false }),

addToFavorites: (item) => set((state) => ({
favorites: [...state.favorites, item]
})),

removeFromFavorites: (id) => set((state) => ({
favorites: state.favorites.filter(item => item.id !== id)
})),

// 异步操作
fetchUser: async (userId) => {
try {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
set({ user: userData });
} catch (error) {
console.error('Failed to fetch user:', error);
}
},

// 复杂计算
getUserProfile: () => {
const { user, favorites } = get();
return {
...user,
favoriteCount: favorites.length,
profileComplete: user?.name && user?.email
};
}
}));

export default useUserStore;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// components/UserProfile.jsx
import React from 'react';
import useUserStore from '../stores/useUserStore';

const UserProfile = () => {
const { user, isLoggedIn, logout } = useUserStore();

if (!isLoggedIn || !user) {
return <div>Please log in</div>;
}

return (
<div className="user-profile">
<h2>Welcome, {user.name}</h2>
<p>Email: {user.email}</p>
<button onClick={logout}>Logout</button>
</div>
);
};

export default UserProfile;

中间件使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';

const useAppStore = create(
devtools(
persist(
(set, get) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 }))
}),
{
name: 'app-storage',
partialize: (state) => ({ count: state.count }) // 只持久化部分状态
}
)
)
);

Jotai

简介

Jotai是由React团队成员Daishi Kato开发的状态管理库,采用了原子(atom)的概念,类似于Recoil的思路,但更轻量和灵活。

  • 特点
    • 基于原子(Atom)的设计
    • 按需订阅,最小化重渲染
    • 类型安全
    • 支持异步状态
    • 与React Suspense兼容

安装与基本使用

1
npm install jotai
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// atoms/userAtoms.js
import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';

// 基础原子
const userAtom = atom(null);
const isLoggedInAtom = atom(false);

// 派生原子
const userInfoAtom = atom(
(get) => {
const user = get(userAtom);
const isLoggedIn = get(isLoggedInAtom);
return { user, isLoggedIn };
},
(get, set, newUserInfo) => {
set(userAtom, newUserInfo.user);
set(isLoggedInAtom, newUserInfo.isLoggedIn);
}
);

// 持久化原子
const favoritesAtom = atomWithStorage('favorites', []);

// 异步原子
const asyncDataAtom = atom(async (get) => {
const userId = get(userAtom)?.id;
if (!userId) return null;

const response = await fetch(`/api/users/${userId}/profile`);
return response.json();
});

export {
userAtom,
isLoggedInAtom,
userInfoAtom,
favoritesAtom,
asyncDataAtom
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// components/UserComponent.jsx
import React from 'react';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import {
userAtom,
isLoggedInAtom,
favoritesAtom,
asyncDataAtom
} from '../atoms/userAtoms';

const UserComponent = () => {
const [user, setUser] = useAtom(userAtom);
const isLoggedIn = useAtomValue(isLoggedInAtom);
const setFavorites = useSetAtom(favoritesAtom);
const asyncData = useAtomValue(asyncDataAtom);

const handleLogin = (userData) => {
setUser(userData);
};

const addToFavorites = (item) => {
setFavorites(prev => [...prev, item]);
};

return (
<div>
{isLoggedIn ? (
<div>
<h2>Welcome {user?.name}</h2>
<pre>{JSON.stringify(asyncData, null, 2)}</pre>
</div>
) : (
<button onClick={() => handleLogin({ id: 1, name: 'John', email: 'john@example.com' })}>
Login
</button>
)}
</div>
);
};

export default UserComponent;

Jotai Selectors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { atom } from 'jotai';
import { selectAtom } from 'jotai/utils';

const userAtom = atom({
id: 1,
name: 'John Doe',
email: 'john@example.com',
profile: {
age: 30,
country: 'USA'
}
});

// 选择器原子 - 只订阅部分状态变化
const userNameAtom = selectAtom(
userAtom,
(user) => user.name
);

const userEmailAtom = selectAtom(
userAtom,
(user) => user.email
);

Recoil

简介

Recoil是Facebook官方推出的React状态管理库,采用原子和选择器(Selector)的模式,旨在解决React内置状态管理的局限性。

  • 特点
    • Facebook官方支持
    • 原子和选择器模式
    • 支持异步数据流
    • 时间旅行调试
    • SSR支持

安装与基本使用

1
npm install recoil
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// atoms/userAtoms.js
import { atom, selector, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';

// 原子状态
export const userState = atom({
key: 'userState',
default: null,
});

export const isLoggedInState = atom({
key: 'isLoggedInState',
default: false,
});

// 选择器 - 派生状态
export const userInfoSelector = selector({
key: 'userInfoSelector',
get: ({ get }) => {
const user = get(userState);
const isLoggedIn = get(isLoggedInState);
return { user, isLoggedIn };
},
set: ({ set }, newValue) => {
set(userState, newValue.user);
set(isLoggedInState, newValue.isLoggedIn);
}
});

// 异步选择器
export const userPreferencesSelector = selector({
key: 'userPreferencesSelector',
get: async ({ get }) => {
const user = get(userState);
if (!user) return null;

const response = await fetch(`/api/users/${user.id}/preferences`);
return response.json();
}
});

// 持久化选择器
export const favoritesState = atom({
key: 'favoritesState',
default: [],
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// components/RecoilUserComponent.jsx
import React from 'react';
import {
useRecoilState,
useRecoilValue,
useSetRecoilState,
useRecoilCallback
} from 'recoil';
import {
userState,
isLoggedInState,
userPreferencesSelector,
favoritesState
} from '../atoms/userAtoms';

const RecoilUserComponent = () => {
const [user, setUser] = useRecoilState(userState);
const isLoggedIn = useRecoilValue(isLoggedInState);
const userPreferences = useRecoilValue(userPreferencesSelector);
const [favorites, setFavorites] = useRecoilState(favoritesState);

const login = useRecoilCallback(({ set }) => (userData) => {
set(userState, userData);
set(isLoggedInState, true);
});

const addToFavorites = (item) => {
setFavorites(prev => [...prev, item]);
};

if (!isLoggedIn) {
return (
<button onClick={() => login({ id: 1, name: 'Alice', email: 'alice@example.com' })}>
Login with Recoil
</button>
);
}

return (
<div>
<h2>Welcome {user?.name}</h2>
<div>Preferences: {JSON.stringify(userPreferences)}</div>
<div>Favorites: {favorites.length}</div>
</div>
);
};

export default RecoilUserComponent;

三者对比分析

架构理念对比

特性ZustandJotaiRecoil
核心概念Store + ActionsAtomAtom + Selector
Provider需求
体积大小~1KB~3KB~13KB
TypeScript支持优秀优秀优秀
异步支持内置内置内置
持久化中间件工具函数第三方

性能对比

性能方面,三个库都表现良好,但各有侧重:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 性能测试示例
import { measure } from 'perf_hooks';

// Zustand - 直接访问,无额外开销
const start1 = measure();
const { count, increment } = useCountStore();
const end1 = measure();

// Jotai - 按需订阅,精确更新
const start2 = measure();
const count = useAtomValue(countAtom);
const end2 = measure();

// Recoil - 选择器优化,最小化重渲染
const start3 = measure();
const count = useRecoilValue(countSelector);
const end3 = measure();

使用场景建议

Zustand适用于:

  • 中小型项目:简单的状态管理需求
  • 快速原型:需要快速搭建状态管理
  • SSR场景:服务端渲染支持良好
  • Redux迁移:类似但更简洁的API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 典型的Zustand使用场景 - 购物车状态
const useCartStore = create((set, get) => ({
items: [],
total: 0,

addItem: (product) => {
const { items } = get();
const existingItem = items.find(item => item.id === product.id);

if (existingItem) {
set({
items: items.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
)
});
} else {
set({
items: [...items, { ...product, quantity: 1 }]
});
}
},

removeItem: (productId) => {
set(state => ({
items: state.items.filter(item => item.id !== productId)
}));
}
}));

Jotai适用于:

  • 精细化控制:需要最小化组件重渲染
  • 复杂状态依赖:多个独立状态原子
  • 渐进式采用:可以逐步引入而不重构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 典型的Jotai使用场景 - 表单状态
import { atom, useAtom, useAtomValue } from 'jotai';

const formDataAtom = atom({});
const isValidAtom = atom(false);
const submissionStatusAtom = atom('idle');

const FormComponent = () => {
const [formData, setFormData] = useAtom(formDataAtom);
const isValid = useAtomValue(isValidAtom);

const updateField = (field, value) => {
setFormData(prev => ({
...prev,
[field]: value
}));
};

return (
<form>
<input
value={formData.name || ''}
onChange={(e) => updateField('name', e.target.value)}
/>
{/* 更多字段... */}
</form>
);
};

Recoil适用于:

  • 大型应用:复杂的状态依赖关系
  • 团队项目:Facebook背书,长期维护保障
  • 异步状态:复杂的异步数据流管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 典型的Recoil使用场景 - 用户权限系统
import { atom, selector, waitForAll } from 'recoil';

const userPermissionsState = atom({
key: 'userPermissionsState',
default: []
});

const userRolesState = atom({
key: 'userRolesState',
default: []
});

// 复合选择器 - 合并权限和角色
const effectivePermissionsSelector = selector({
key: 'effectivePermissionsSelector',
get: ({ get }) => {
const permissions = get(userPermissionsState);
const roles = get(userRolesState);

return {
permissions,
roles,
canViewDashboard: permissions.includes('view_dashboard'),
canManageUsers: permissions.includes('manage_users') || roles.includes('admin')
};
}
});

最佳实践

1. 状态分层管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 通用状态 - 全局
const globalModalState = atom({
key: 'globalModalState',
default: { isOpen: false, content: null }
});

// 页面状态 - 局部
const currentPageState = atom({
key: 'currentPageState',
default: 'dashboard'
});

// 组件状态 - 局部
const localComponentState = atom({
key: 'localComponentState',
default: { expanded: false }
});

2. 错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Zustand - 错误处理
const useApiStore = create((set) => ({
data: null,
error: null,
loading: false,

fetchData: async () => {
set({ loading: true, error: null });
try {
const response = await fetch('/api/data');
const data = await response.json();
set({ data, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
}
}));

// Jotai - 错误处理
const dataAtom = atom(null);
const errorAtom = atom(null);
const loadingAtom = atom(false);

// Recoil - 异常处理选择器
const safeDataSelector = selector({
key: 'safeDataSelector',
get: async ({ get }) => {
try {
const response = await fetch('/api/data');
if (!response.ok) throw new Error('Failed to fetch');
return await response.json();
} catch (error) {
return { error: error.message };
}
}
});

3. 性能优化

1
2
3
4
5
6
7
8
9
10
11
12
// 避免不必要的重渲染
// 不好的做法
const BadComponent = () => {
const state = useRecoilValue(complexState); // 整个状态对象发生变化时重渲染
return <div>{state.property}</div>;
};

// 好的做法 - 只订阅需要的部分
const GoodComponent = () => {
const property = useRecoilValue(selectAtom(complexState, s => s.property));
return <div>{property}</div>;
};

生态与社区

Zustand生态

  • 中间件:devtools, persist, subscribeWithSelector
  • 绑定库:zustand-x(Redux DevTools),@redux-devtools/fluxible
  • 社区活跃:GitHub Star 30K+,持续更新

Jotai生态

  • 工具库:jotai/valtio(Proxy集成),jotai/urql(GraphQL集成)
  • 插件系统:丰富的utils库
  • 社区发展:快速增长的社区支持

Recoil生态

  • 官方支持:Facebook维护
  • 配套工具:Recoil DevTools
  • 文档完善:官方文档详细

选择合适的状态管理库需要根据项目规模、团队经验、性能要求和维护成本综合考虑。Zustand适合快速开发,Jotai适合精细控制,Recoil适合大型应用。

总结

  Zustand、Jotai和Recoil各有特色,都为React状态管理提供了现代化的解决方案。Zustand以其极简主义和易用性脱颖而出,适合大多数项目;Jotai通过原子化状态提供更精确的控制;Recoil则凭借Facebook的支持和成熟的生态系统适合大型企业级应用。

  在选择时,建议:

  1. 从小型项目开始,逐步演进
  2. 考虑团队的学习成本
  3. 关注库的维护状态和社区活跃度
  4. 考虑与现有技术栈的集成成本

  随着前端技术的发展,状态管理方案也在不断演进,未来可能会出现更多创新的解决方案,开发者需要保持关注并适时调整技术选型。

bulb