企业应用的最佳React状态管理工具
Teimur是React的导师,也是Toptal核心团队的高级前端工程师。
企业级React应用程序的开发者知道状态管理对于连贯的最终用户体验是多么重要。
然而,用户并不是唯一受状态管理影响的人。React开发人员创建和维护状态。他们希望状态管理是简单的、可扩展的和原子性的。React已经通过引入钩子向这个方向发展。
当状态应该在许多组件之间共享时,可能会出现问题。工程师们必须找到适合他们需求的工具和库,但同时又要满足企业级应用所需的高标准。
在这篇文章中,我将分析和比较最流行的库,为企业级应用的状态管理挑选最合适的库。
内置的React状态管理功能
React有一个优秀的工具来提供跨多个组件的数据。Context的主要目标是避免道具的钻取。我们的目标是得到一个易于使用的工具来管理企业应用中可能遇到的各种情况下的状态:频繁的更新、重新设计、引入新功能等等。
虽然所有这些理论上都可以用Context来做,但它需要一个定制的解决方案,需要时间来设置、支持和优化。Context的唯一优势是它不依赖于第三方库,但这不能超过维护这种方法的努力。
此外,React团队成员Sebastian Markbage提到,新的Context API不是为高频更新而建立和优化的,而是为低频更新,如主题更新和认证管理而建立的。
检查现有的库
GitHub上有几十个状态管理工具(例如Redux、MobX、Akita、Recoil和Zustand)。然而,把它们每个都考虑进去会导致无尽的研究和比较。这就是为什么我把我的选择范围缩小到三个主要的竞争对手,基于他们的受欢迎程度、使用情况和维护者。
为了使比较明确,我将使用以下质量属性:
- 可用性
- 可维护性
- 性能
- 可测试性
- 可扩展性(在更大的状态下以同样的性能工作)
- 可修改性
- 可重用性
- 生态系统(有各种辅助工具来扩展功能)
- 社区(有大量的用户,他们的问题在网上得到了解答)
- 可移植性(可与React以外的库/框架一起使用)
Redux
Redux是一个创建于2015年的状态容器。它变得疯狂地流行,因为:
- 在它推出的时候,没有认真的替代品。
- 它提供了状态和动作之间的分离。
react-redux神奇地实现了直截了当的状态连接。- 该库的共同创造者是著名的Facebook开发者和React核心团队成员Dan Abramov。
你有一个全局存储,你的数据就在那里。每当你需要更新存储时,你就会派发一个动作,这个动作会被送到reducer那里。根据动作的类型,还原器以不可变的方式更新状态。
要在React中使用Redux,你需要通过react-redux ,将组件订阅给商店的更新。
Redux API示例
Redux在代码库中区别于其他工具的基本部分是分片。它们包含了行动和还原器的所有逻辑。
// slices/counter.js
import { createSlice } from "@reduxjs/toolkit";
export const slice = createSlice({
name: "counter",
initialState: {
value: 0
},
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
}
}
});
export const actions = slice.actions;
export const reducer = slice.reducer;
// store.js
import { configureStore } from "@reduxjs/toolkit";
import { reducer as counterReducer } from "./slices/counter";
export default configureStore({
reducer: {
counter: counterReducer
}
});
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import App from './App'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
// App.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { actions } from "./slices/counter";
const App = () => {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<div>
<button onClick={() => dispatch(actions.increment())}>Increment</button>
<span>{count}</span>
<button onClick={() => dispatch(actions.decrement())}>Decrement</button>
</div>
</div>
);
};
export default App;
质量属性
- 可用性:随着官方工具包包的引入,Redux变得非常简单。你创建一个片断(初始状态、还原器和动作的组合),将其传递给商店,并通过钩子在组件中访问它。
- 可维护性:Redux很简单。它不需要很深的知识来了解如何增强或修复一些东西。
- 性能:Redux的主要性能影响者是软件工程师。Redux是一个简单明了的工具,没有太多的逻辑。如果你看到状态更新很慢,你可以按照官方的指导方针使其更快。
- 可测试性:Redux由纯函数组成(动作和还原器),使其非常适合单元测试。它还提供了编写集成测试的机制,其中存储、动作和还原器一起工作。
- 可扩展性:默认情况下,Redux有一个全局状态,使其难以扩展。然而,有一个redux-dynamic-modules库,可以创建模块化的还原器和中间件。
- 可修改性:由于Redux支持中间件,定制Redux是一件毫不费力的事情。
- 可重用性:Redux与框架无关,所以它的可重用性非常好。
- 生态系统:Redux提供了一个巨大的生态系统,包括有用的附加组件、库和工具。
- 社区:Redux是我们比较中最古老的状态管理库,它已经积累了一个庞大的社区,有一个重要的知识库。在Stack Overflow上有约30,000个(约19,000个已回答)带有
redux标签的问题。 - 脉冲:Redux会定期更新和维护。
MobX
MobX是另一个相对较老的库,在GitHub上有23,000颗星。它与Redux的不同之处在于,它遵循OOP范式并使用可观察变量。MobX由Michel Weststrate创建,目前由一群开源爱好者在波士顿的Mendix公司的帮助下维护。
在MobX中,你创建一个JavaScript类,在构造函数中调用makeObservable ,这是你的可观察存储(如果你有合适的加载器,你可以使用@observable 装饰器)。然后你声明这个类的属性(状态)和方法**(动作和计算值**)。组件订阅这个可观察存储,以访问状态、计算值和动作。
MobX的另一个基本特征是可变性。它允许在你想避免副作用的情况下默默地更新状态。
MobX API实例
MobX的一个独特的特点是,您可以创建几乎是纯ES6类,所有的魔法都隐藏在引擎盖下。它需要较少的库专用代码,以保持对逻辑的专注。
// stores/counter.js
import { makeAutoObservable } from "mobx";
class CounterStore {
value = 0;
constructor() {
makeAutoObservable(this);
}
increment() {
this.value += 1;
}
decrement() {
this.value -= 1;
}
}
export default CounterStore;
// index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "mobx-react";
import App from "./App";
import CounterStore from "./stores/counter";
ReactDOM.render(
<Provider counter={new CounterStore()}>
<App />
</Provider>,
document.getElementById("root")
);
// App.js
import React from "react";
import { inject, observer } from "mobx-react";
const App = inject((stores) => ({ counter: stores.counter }))(
observer(({ counter }) => {
return (
<div>
<div>
<button onClick={() => counter.increment()}>Increment</button>
<span>{counter.value}</span>
<button onClick={() => counter.decrement()}>Decrement</button>
</div>
</div>
);
})
);
export default App;
质量属性
- 可用性:一个可观察的存储是状态管理的单一入口。它使MobX的使用变得简单,因为你有唯一的地方可以修改。
- 可维护性:这是一个相当大的缺点。如果没有RxJS API的知识,你将无法达到预期的效果。在一个不合格的团队中使用MobX可能会导致状态不一致的麻烦。
- 性能:MobX由独立的商店组成,并使您能够只订阅您需要的商店。这是很有效的。
- 可测试性:可观察的商店是普通的JavaScript对象,里面隐藏着反应性功能。测试与任何其他JavaScript类相同。
- 可扩展性:可观察到的存储空间被逻辑地分割;在扩展MobX方面没有任何困难。
- 可修改性:MobX允许创建具有修改行为的自定义观测器。此外,还有一个叫做反应的概念。反应模拟自动的副作用。这些东西使得MobX非常容易定制。
- 可重用性:MobX与框架无关,所以它的可重用性非常好。
- 生态系统:有数以百计的扩展可用于MobX。
- 社区:MobX有很多忠实的粉丝。在Stack Overflow上有~1,600个(~1,000个已回答)带有
mobx标签的问题。 - 脉冲:MobX被定期更新和维护。
Recoil
Recoil是一个相对较新的项目,是React团队的最新创意。它背后的基本想法是简单实现缺失的React功能,如共享状态和衍生数据。
你可能想知道为什么一个实验性的库要为企业级项目进行审查。首先,Recoil是目前React社区中讨论最多的话题之一。其次,Recoil得到了Facebook的支持,并且已经在其一些应用程序中使用,这意味着它将在某个时候成为一个稳定的版本。最后,这是一种在React中共享状态的全新方法,我相信即使Recoil被废弃,也会有另一种工具遵循同样的路径。
Recoil是建立在两个术语之上的:原子和选择器。原子是一个共享状态的片段。一个组件可以订阅一个原子来获取/设置其值。
正如你在图片中看到的,当值被改变时,只有被订阅的组件被重新渲染。这使得Recoil的性能非常好。
Recoil开箱即用的另一个好东西是选择器。选择器是一个由原子或其他选择器聚合而成的值。对于消费者来说,原子和选择器之间没有区别,他们只需要订阅一些反应性部分并使用它。
每当一个原子/选择器被改变时,使用它的选择器(即被订阅到它)就会被重新评估。
Recoil API示例
Recoil的代码远比它的竞争对手不同。它基于React钩子,更注重于状态结构而不是突变这个状态。
// atoms/counter.js
import { atom } from "recoil";
const counterAtom = atom({
key: "counter",
default: 0
});
export default counterAtom;
// index.js
import React from "react";
import ReactDOM from "react-dom";
import { RecoilRoot } from "recoil";
import App from "./App";
ReactDOM.render(
<RecoilRoot>
<App />
</RecoilRoot>,
document.getElementById("root")
);
// App.js
import React from "react";
import { useRecoilState } from "recoil";
import counterAtom from "./atoms/counter";
const App = () => {
const [count, setCount] = useRecoilState(counterAtom);
return (
<div>
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<span>{count}</span>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
</div>
);
};
export default App;
质量属性
- 可用性:Recoil是最容易使用的工具之一,因为它像React中的
useState。 - 可维护性:在Recoil中你所要做的就是维护组件中的选择器和钩子--更多价值,更少模板。
- 性能:Recoil在React之外建立了一个状态树。状态树使你能够获得和倾听你需要的东西,而不是整个树的变化。它在内部也得到了很好的优化。
- 可测试性:Recoil提供了一个测试其原子和选择器的机制。
- 可扩展性:一个分裂成多个独立片段的状态使它在可扩展性方面有很好的表现。
- 可修改性:Recoil只负责存储值和它们的聚合。它没有数据流,所以它可以很容易地被定制。
- 可重用性:Recoil 依赖于 React。它不能在其他地方重复使用。
- 生态系统:目前没有Recoil的生态系统。
- 社区:Recoil太新了,没有一个大的社区。Stack Overflow上有大约70个带有
recoiljs标签的问题。 - 脉冲:Recoil的更新频率很低(最近的两次更新之间有6个月的时间)。它在GitHub上也有大量的开放问题。
选择正确的React状态管理工具
当涉及到企业级应用时,这些React全局状态管理库是如何叠加的?
Recoil很年轻,很新鲜,但目前还没有社区和生态系统。尽管Facebook正在研究它,而且API似乎很有前途,但一个巨大的React应用不能依赖一个社区支持薄弱的库。此外,它是实验性的,使它更加不安全。对于现在的React企业应用来说,它绝对不是一个好的选择,但值得关注。
MobX和Redux不存在这些问题,市场上大多数大公司都使用它们。它们之间的不同之处在于学习曲线。MobX需要对反应式编程有基本了解。如果参与项目的工程师不够熟练,应用程序最终可能会出现代码不一致、性能问题和增加开发时间。如果你的团队对反应性有认识,MobX是可以接受的,并且会满足你的需求。
Redux也有一些问题,主要是关于可扩展性和性能。然而,与MobX不同,这些问题都有成熟的解决方案。
考虑到每一个优点和缺点,并考虑到我的个人经验,我推荐Redux作为React企业级应用程序的最佳选择。
了解基础知识
状态管理在React中是必要的吗?
React是一个生成可视化界面的库。视觉表现直接取决于状态。换句话说,状态对用户看到的东西负责。你的状态的任何变化都会立即显示给用户。
哪种状态管理在React中是最好的?
React的useState是本地状态管理的最佳选择。如果你需要一个全局状态解决方案,最流行的是Redux、MobX和内置的Context API。你的选择将取决于你的项目规模,你的需求,以及你的工程师的专业技能。
你如何在React中维护全局状态?
如果你保持全局状态的干净,维护它是很容易的。它应该只包含那些在多个松散连接的组件中共享的项目。
为什么不使用React Context API?
Context API的功能是开箱即用的。它不是为高频率的更新而建立和优化的,而是为低频率的更新,如主题更新和认证管理。
Redux需要很多模板代码吗?
随着官方Redux工具包的引入,描述Redux中的状态管理已经变得简明。
我应该怎样做才能熟练掌握MobX?
MobX是用RxJS实现的全局状态管理。如果你想成为一个王牌,请学习RxJS。这不仅对理解单个库有帮助,而且对整个反应性的概念也有帮助。
Recoil适合生产吗?
Recoil表现良好,即使是在实验阶段。它对大型应用来说并不理想,但对不太依赖状态的小型应用会很有用。