React状态管理库对比

对比React常见的状态管理库context、Redux、MobX、zustand、jotai以及介绍它们的使用场景

context

context被创造出来就是为了解决prop drilling的问题,但是它存在重复渲染的问题,可以用https://github.com/dai-shi/use-context-selector解决这个问题,也可以不依赖任何库去解决use-context-selector

1
2
3
4
5
6
7
8
9
10
11
const ThemeContext = createContext('light');
const Main = ({ children }) => {
return (
<ThemeContext.Provider value={useState('light')}>
{children}
</ThemeContext.Provider>
)
}
function App() {
const theme = useContext(ThemeContext);
}

Redux + React-Redux

相对于context,redux是基于单向数据流,遵循不可变数据的原则性能较好易于调试、易于扩展、middleware支持、生态较好,开发调试devtool比较方便,但是上手成本高,需要理解单向数据流的概念。

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
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})
configureStore({
reducer: {
counter: counterSlice.reducer,
},
})
function Counter() {
const count = useSelector((state) => state.counter.value)
const dispatch = useDispatch()
return (
<div>
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
)
}

zustand

相对于Redux有什么优势,参考官方

  1. zustand概念和使用上简单
  2. Redux要使用Provider
  3. zustand可以transient updates,就是组件能监听状态变更并且不造成重复渲染。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const useBearStore = create((set) => ({
    bears: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
    removeAllBears: () => set({ bears: 0 }),
    }))
    function BearCounter() {
    const bears = useBearStore((state) => state.bears)
    const increasePopulation = useBearStore((state) => state.increasePopulation)
    return (<div>
    <h1>{bears} around here ...</h1>
    <button onClick={increasePopulation}>one up</button>
    </div>)
    }

MobX

MobX通过透明的函数响应式编程使得状态管理变得简单和可扩展。MobX背后的哲学很简单: 任何源自应用状态的东西都应该自动地获得,其中包括UI、数据序列化等等,核心重点就是: MobX通过响应式编程实现简单高效,可扩展的状态管理。

相对于react-redux,MobX的优势主要在开发简单,不用太多样板代码,但是因为状态可以在多处修改导致追踪状态比较麻烦,并且MobX不需要selector就能避免re-reneder,设置值的方式也不需要像Zustand那样必须要set(state => newState),更符合直觉,体验更好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Timer {
secondsPassed = 0
constructor() {
makeAutoObservable(this)
}
increaseTimer() {
this.secondsPassed += 1
}
}
const myTimer = new Timer()
const TimerView = observer(({ timer }) => <span>Seconds passed: {timer.secondsPassed}</span>)
ReactDOM.render(<TimerView timer={myTimer} />, document.body)
setInterval(() => {
myTimer.increaseTimer()
}, 1000)

jotai

jotai上手简单,生态较完善,并且DX比较不错,使用不需要层层传递props,而且不像Context会重复渲染,缺点在复杂应用中状态太多不好管理,需要在上层封装Model、Controller层。

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
import { Suspense } from 'react'
import { atom, useAtom } from 'jotai'
// 定义原子状态
const userIdAtom = atom(1)
// 派生状态
const userAtom = atom(async (get, { signal }) => {
const userId = get(userIdAtom)
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}?_delay=2000`,
{ signal },
)
return response.json()
})
const Controls = () => {
// 使用atom
const [userId, setUserId] = useAtom(userIdAtom)
return (
<div>
User Id: {userId}
<button onClick={() => setUserId((c) => c - 1)}>Prev</button>
<button onClick={() => setUserId((c) => c + 1)}>Next</button>
</div>
)
}

const UserName = () => {
const [user] = useAtom(userAtom)
return <div>User name: {user.name}</div>
}

valtio

updating…

何时使用

Context + reducer

不想依赖第三方库,项目比较简单

MobX

快速开发和快速PMF,中大型项目也可以

zustand

快速开发和快速PMF,中大型项目也可以,需要undo/redo

React-Redux

项目复杂,需要长期维护,undo/redo

jotai

中小型项目

参考资料

https://medium.com/@brechtcorbeel/understanding-the-core-principles-of-react-a-comprehensive-introduction-cb56f0435ea1
https://medium.com/@padmagnanapriya/state-management-in-react-comparing-context-api-redux-0403748a241f
https://juejin.cn/post/6973977847547297800?searchId=20250421092546DCBB5357F0DDC339B843#heading-11
https://cn.redux.js.org/faq/immutable-data/#what-are-the-benefits-of-immutability