Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【前端状态管理】React状态管理工具优劣势分析 react hooks context / redux / mobx / zustand / jotai / recoil / valtio #81

Open
AwesomeDevin opened this issue Jan 16, 2023 · 0 comments

Comments

@AwesomeDevin
Copy link
Owner

AwesomeDevin commented Jan 16, 2023

什么是状态管理?

“状态”是描述应用程序当前行为的任何数据。这可能包括诸如“从服务器获取的对象列表”、“当前选择的项目”、“当前登录用户的名称”和“此模式是否打开?”等值。

众所周知,我们在研发一个复杂应用的过程中,一套好的状态管理方案是必不可少的,既能提升研发效率,又能降低研发维护成本,那么状态管理方案那么多,它们有什么不同,我们又该如何选择适合当前应用的方案呢?

本期将主要就 react 的常用状态管理方案进行对比分析,希望对各位看客有帮助。

React 状态管理方案

方案介绍
方案对比
框架 原理 优点 缺点
hooks context 基于 react hook,开发者可实现内/外部存储 1. 使用简单
2. 不需要引用第三方库,体积最小
3. 支持存储全局状态,但在复杂应用中不推荐
4. 不依赖 react 上下文,可在组件外调用(外部存储的条件下)
1. context value发生变化时,所有用到这个context的组件都会被重新渲染,基于 content 维护的模块越多,影响范围越大。
2.依赖 Context Provider 包裹你的应用程序,修改 store 无法在应用最顶层(App.tsx 层级)触发渲染
3. 受ui框架约束(react)
4. 依赖hooks调用
react-redux Flux思想,发布订阅模式,遵从函数式编程,外部存储 1. 不依赖 react 上下文,可在组件外调用
2. 支持存储全局状态
3. redux 本身是一种通用的状态解决方案
1. 心智模型需要一些时间来理解,特别是当你不熟悉函数式编程的时候
2. 依赖 Context Provider 包裹你的应用程序,修改 store 无法在应用最顶层(App.tsx 层级)触发渲染
3.受 ui 框架约束(react)
mobx 观察者模式 + 数据截止,外部存储 1. 使用简单,上手门槛低
2. 不依赖 react 上下文,可在组件外调用
3. 支持存储全局状态
4.通用的状态解决方案
1.可变状态模型,某些情况下可能影响调试
2. 除了体积相对较大之外,笔者目前未感觉到较为明显的缺点,3.99M
zustand Flux思想,观察者模式,外部存储 1. 轻量,使用简单,上手门槛低
2. 不依赖 react 上下文,可在组件外调用
3. 支持存储全局状态
4. 通用的状态解决方案
1.框架本身不支持 computed 属性,但可基于 middleware 机制通过少量代码间接实现 computed ,或基于第三方库 zustand-computed 实现
jotai 基于 react hook,内部存储 1. 使用简单
2. 组件颗粒度较细的情况下,jotai性能更好
3.支持存储全局状态
1. 依赖 react 上下文, 无法组件外调用,相对而言, zustand 在 react 环境外及全局可以更好地工作
2.受ui框架约束(react)
recoil 进阶版 jotai,基于 react hook + provider context,内部存储 相对于 jotai而言,会更重一些,但思想基本不变,拥有一些 jotai 未支持的特性及 api,如:
1.监听 store 变化
2. 针对 atom 的操作拥有更多的 api,编程上拥有更多的可能性,更加有趣
拥有 jotai 所有的缺点,且相对于 jotai 而言:
1.使用 recoil 需要 < RecoilRoot > 包裹应用程序
2. 编写 selector 会复杂一些
valtio 基于数据劫持,外部存储 1. 使用简单,类mobx(类vue)的编程体验
2.支持存储全局状态
3.不依赖 react 上下文,可在组件外调用
4. 通用的状态解决方案
1.可变状态模型,某些情况下可能影响调试
2.目前笔者没发现其它特别大的缺点,个人猜测之所以star相对zustand较少,是因为 valtio 的数据双向绑定思想与 react 存在冲突。

Source

import React from "react"
import ReactDOM from "react-dom"
import { makeAutoObservable } from "mobx"
import { observer } from "mobx-react"

// 状态及相关事件
class Timer {
    secondsPassed = 0

    constructor() {
        makeAutoObservable(this)
    }

    increase() {
        this.secondsPassed += 1
    }

    reset() {
        this.secondsPassed = 0
    }
}

const myTimer = new Timer()

// 构建可观擦组件
const TimerView = observer(({ timer }) => (
    <button onClick={() => timer.reset()}>Seconds passed: {timer.secondsPassed}</button>
))

ReactDOM.render(<TimerView timer={myTimer} />, document.body)

// 触发更新事件
setInterval(() => {
    myTimer.increase()
}, 1000)
  • zustand
import { create } from 'zustand'

// 状态及相关事件
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)
  return <h1>{bears} around here ...</h1>
}

// 触发更新事件
function Controls() {
  const increasePopulation = useBearStore((state) => state.increasePopulation)
  return <button onClick={increasePopulation}>one up</button>
}
  • jotai
import { atom } from 'jotai'

const countAtom = atom(0)

function Counter() {
  // 状态及相关事件
  const [count, setCount] = useAtom(countAtom)
  return (
    <h1>
      {count}
      <button onClick={() => setCount(c => c + 1)}>one up</button>
    </h1>
  )
}
  • recoil
const fontSizeState = atom({  
  key: 'fontSizeState',  
  default: 14,  
});
function FontButton() {  
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);  
  return (  
    <button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>  
      Click to Enlarge  
    </button>  
  );  
}
  • valtio
import { proxy, useSnapshot } from 'valtio'

const state = proxy({ count: 0, text: 'hello' })

function Counter() {
  const snap = useSnapshot(state)
  return (
    <div>
      {snap.count}
      <button onClick={() => ++state.count}>+1</button>
    </div>
  )

相关建议

  1. 如果你需要useState+useContext的替代品,那么jotai非常适合,即原子化的组件状态管理或少量组件间状态共享。
  2. 如果你习惯了redux或喜欢react的自然不可变更新,那么zustand将非常适合。
  3. 如果你习惯了vue/ slute /mobx,或者是JS/React的新手,valtio的可变模型将很适合。
  4. 如果你在使用 zustand(redux/等不可变数据模型) + immer,建议改用valtio(mobx)
  5. mobx有actions概念,而valtio概念更为简单(自由),如果你希望工程更为规范,可以使用mobx,如果是希望工程更为自由便捷,可以使用valtio
  6. recoiljotai的编程思想类似,但提供了更多的 api 与 特性,针对原子状态拥有更多的可操作性,同时包体积也更大,但由于recoil功能庞大,其使用相对于jotai会繁琐一些,如果你希望工程轻巧便捷可以选用jotai,如果你想试试原子状态更多的可能性,那么试试recoil吧。

如果该文章对你有帮助,请给我点个👍吧~
下期将带来Vue状态管理工具优劣势分析, 欢迎关注我的Blog 🌟

@AwesomeDevin AwesomeDevin changed the title 【前端状态管理】React状态管理工具优劣势分析 hooks context / redux / mobx / zustand / jotai / valtio 【前端状态管理】React状态管理工具优劣势分析 react hooks context / redux / mobx / zustand / jotai / valtio Jan 16, 2023
@AwesomeDevin AwesomeDevin changed the title 【前端状态管理】React状态管理工具优劣势分析 react hooks context / redux / mobx / zustand / jotai / valtio 【前端状态管理】React状态管理工具优劣势分析 react hooks context / redux / mobx / zustand / jotai / recoil / valtio Jan 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant