【译】Recoil vs Redux | 最终的 React 状态方案 PK

4,751 阅读10分钟

原文链接: medium.com/@chandan.re…

image.png

Redux vs Recoil

React with its component-based approach has made the life of developers easy by managing separate state and logic for each component and the ability to re-use them when required. But what if you wanted to use a shared state between multiple components or maybe fetch data from an API once and make it available to all the components in your app? That’s when global state management libraries like Redux, MobX, etc., come into play.

React 基于组件的方法通过管理每个组件的独立状态和逻辑,以及在需要时复用它们的能力,简化了开发人员的工作。但如果你想在多个组件之间使用共享状态,或者从一个 API 中获取一次数据,让应用中的所有组件都可以使用该状态呢?这时,Redux、MobX等全局状态管理库就开始发挥作用了。

The downfall of Redux:

With the release of React 16.3, the react community got to experience the new Context API which worked similar to Redux and allowed to manage state in multiple components using Context Object, Provider, and Consumer. However, the context API also came with a catch! Sebastian Markbage from the React team has mentioned that the new Context API was not built and optimized for high-frequency updates but rather for low-frequency updates like theme and user auth management inside your app. You can check out his comment here. Context API also had some limitations with code splitting and the ability to store indefinite values instead of a single value.

Redux 的衰败

随着 React 16.3的发布,React 社区体验到了新的上下文 API,它的工作原理类似于 Redux,允许使用上下文对象、Provider 和 Consumer 在多个组件中管理状态。然而,上下文 API 还附带了一个缺陷! React 团队的 Sebastian Markbage 提到,新的 Context API 并没有针对高频更新进行构建和优化,而是针对应用内部的主题和用户认证管理等低频更新。他的评论在 这里。 上下文 API 在代码分割和存储不确定值(而不是单个值)方面也有一些限制。

Quick look at problems with redux:

  • Steep learning curve
  • Too much boilerplate code
  • Re-structure your project
  • Lacks concurrent mode support
  • Non — reactish approach
  • Difficult to achieve code-splitting
  • No built-in async support

Checkout this article by Alexandr Zavalii to know what’s wrong with redux and context api in detail.

快速看一下 redux 的问题:

  • 陡峭的学习曲线
  • 太多的样板代码
  • 重新组织你的项目
  • 缺乏并发模式支持
  • 非反应性方法
  • 难以实现代码拆分
  • 没有内置的异步支持

查看 Alexandr Zavalii 的这篇文章,了解 redux 和上下文 api 的详细错误。

译者注: 观点有些许的片面,并发模式支持的库没几个,因为外部的变化是不会触发更新的。

So, what’s next?

Facebook recently launched Recoil, which is a brand new experimental JavaScript state management library that addresses many of the problems larger applications face when using the existing Context API.

下一个是什么?

Facebook最近推出了Recoil,这是一个全新的实验性 JavaScript 状态管理库,可解决大型应用程序在使用现有Context API 时面临的许多问题。

image.png

Recoil (Fan made logo — Chandan.dev)

The Basics of Recoil:

Recoil mainly comprises of two things — Atoms and Selectors.

Recoil 的基本原理:

Recoil 主要由两种东西组成——AtomsSelectors

Atoms:

Atoms are units of state. They’re updateable and subscribable: when an atom is updated, each subscribed component is re-rendered with the new value. They can be created at runtime, too. Atoms can be used in place of React local component state. If the same atom is used from multiple components, all those components share their state.

Atoms:

Atoms 是状态单位。它们是可更新和可订阅的: 当一个 atom 被更新时,每个订阅的组件都用新值重新 render。它们也可以在运行时创建。Atoms 可以用来代替 React 局部的组件状态。如果在多个组件中使用同一个 atom,那么所有这些组件将共享它们的状态。

In simpler terms, Atoms are units of state that components can subscribe to.

简单地说,Atoms 是组件可以订阅的状态单元。

Selectors:

A selector is a pure function that accepts atoms or other selectors as input. When these upstream atoms or selectors are updated, the selector function will be re-evaluated. Components can subscribe to selectors just like atoms, and will then be re-rendered when the selectors change. Selectors can also be used to calculate derived data that is based on state.

Selectors:

Selectors 是接受 atoms 或其他 selectors 作为输入的纯函数。当这些上游的 atoms 或 selectors 被更新时,selectors 函数将被重新计算。组件可以像 atom 一样订阅 selectors,当 selectors 发生变化时,组件将被重新 render。selectors 还可以用于计算基于 state 的衍生数据。

In simpler terms, Selectors transform the atom state either synchronously or asynchronously.

简单地说,选择器以同步或异步方式转换原子状态。

译者注: 异步的 selector 是一个很吸引人的亮点,但是他的定位和社区中 selector 的概念出现了偏差,承担了一定副作用的功能。

You should probably check out this video from the recent React Europe live stream to get a better understanding of recoil.

你或许应该看看最近 React Europe 的视频,以更好地理解 recoil。

image.png

React Recoil at ReactEurope — 2020

The face-off: Recoil vs Redux

Let’s start by creating a demo application with

create-react-app

which increments the count on the click of a button.

PK: Recoil vs Redux

让我们先用 create-react-app 创建一个演示应用程序,它会增加点击按钮的次数。

image.png

Demo app for recoil vs redux

Our application is going to consist of

MainComponent.js

which receives the

count

and

handleFireClick

function as props.

我们的应用程序将由 MainComponent.js 组成,它接收 count 和 handleFireClick 函数作为 props。

image.png

Configuring the store:

In redux, we start off by creating a basic store that acts like a global state for our application.

配置 store:

在 redux 中,我们首先创建一个基本 store,它充当应用程序的全局状态。

image.png

In recoil, there is no need to create a separate store. Wow! that’s awesome 🤩.

在 recoil 中,不需要创建单独的 store。哇!太棒了🤩。

Creating a shared state:

In redux, we create the desired application state using reducers. Let’s create a simple

counterReducer

which increments the count by 1.

创建共享状态:

在 redux 中,我们使用 reducer 创建所需的应用程序状态。让我们创建一个简单的 counterReducer,它将计数增加1。

image.png

In recoil, creating a shared state is a straight forward approach using atoms. Each atom can be considered as a single shared piece of state. Now, let’s create an atom for holding our counter state.

在 recoil 中,使用 atoms 直接创建共享状态。每个 atoms 都可以看作是一个单一的共享状态块。现在,让我们创建一个 atoms 来保存我们的计数器状态。

image.png

Firing the actions:

In redux, actions are fired using the dispatch method provided by redux. Let’s create a file named

actions.js

which holds the action for incrementing the count.

触发操作:

在 redux 中,使用 redux 提供的 dispatch 方法触发 actions。让我们创建一个名为 actions.js 的文件,它保存了增加计数的操作。

image.png

In recoil, we fire off actions and modify the existing shared atom state using selectors.

在 recoil 中,我们使用 selectors 触发 actions 并修改现有的共享 atom 状态。

image.png

Connect ’em all:

Finally, it’s time to connect the shared state with our

MainComponent.js

component and to differentiate Redux from Recoil, I’ll be creating two new wrapper components called

ReduxExample.js

and

RecoilExample.js

.

In

ReduxExample.js,

we use**

useSelector

and

useDispatch

**

hooks

provided by react-redux to get value from store and fire actions to update it.

连接他们:

最后,是时候将共享状态连接到我们的 MainComponent.js 组件了,为了区分 Redux 和 Recoil,我将创建两个新的包装器组件 ReduxExample.js 和 RecoilExample.js。

在 ReduxExample.js 中。我们使用 react-redux 提供的 useSelector 和 useDispatch hooks 从 store 中获取值,并触发动作来更新它。

image.png

Bonus: In

ReduxExample.js

we can also create a class component and wrap the component using connect HOC by redux and pass the shared state, actions as props using**

mapStateToProps

, and

mapDispatchToProps

**respectively.

**额外的好处:**在 ReduxExample.js 中,我们还可以创建一个类组件,并使用 redux 的 connect HOC 封装组件,并分别使用 mapStateToProps 和 mapDispatchToProps 将共享状态、actions 作为 props 传递。

image.png

In

RecoilExample.js

we can directly use the shared atom state value with**

useRecoilValue

andupdate our state, as simple as doing a setState but with

useRecoilState

**.

在 RecoilExample.js 中,我们可以直接使用 useRecoilValue 共享 atom 状态值,并更新我们的状态,就像使用useRecoilState 执行 setState 一样简单。

image.png

Note: Recoil is based on react hooks and will only work for functional components.

注意: Recoil 基于 react hooks,只对 function 组件有效。

Final Wrap:

There is just one more step for your app to start working and that’s by wrapping your Example components with HOC components provided by Redux and Recoil.

For the Redux example, we use the Provider from react-redux and pass our

ReduxExample.js

component as children. Make sure to also supply the store you created in the first step.

最后的封装:

还有一个步骤让你的应用程序开始工作,那就是用 Redux 和 Recoil 提供的特殊组件包装你的示例组件。

对于 Redux 的例子,我们使用 react-redux 的 Provider,并将 ReduxExample.js 组件作为子组件传递。确保还提供了在第一步中创建的 store。

image.png

Similarly, for Recoil, we wrap the

RecoilExample.js

component using the RecoilRoot component provided by recoil.

类似地,对于 Recoil,我们包装一下 RecoilExample.js 组件。使用 recoil 提供的 RecoilRoot 组件。

image.png

Let’s Go!

At this point, we have successfully created the shared state and actions to update it using both redux and recoil. Simply run npm run start and check if your code is working.

我们开始吧!

至此,我们已经成功地创建了共享状态和操作,并使用 redux 和 recoil 来更新它。只需运行 npm run start,然后检查你的代码是否正常工作。

image.png

The main question — Will Recoil replace Redux?

That’s a tough one to answer today (at the time of writing this post) as it is still in the experimental phase but the new library looks promising and there is a high probability that developers will switch to Recoil from Redux shortly.

Why? Recoil lets you create a data-flow graph that flows from atoms (shared state) through selectors (pure functions) and down into your React components without having to deal with store creations and re-render the entire App component tree while the state updates.

主要的问题是 —— Recoil 会取代 Redux 吗?

这个问题在今天很难回答(在写这篇文章的时候),因为它仍然处于实验阶段,但是新的库看起来很有希望,开发人员很快就会从 Redux 转向 Recoil。

为什么? Recoil 允许你创建一个数据流图,从 atoms (共享状态)通过 selectors (纯函数)向下进入你的 React 组件,而不必处理创建的 store 和重新渲染整个应用组件树,而状态更新。

Conclusion:

So, this was a quick comparison between recoil and redux for a simple increment counter app. You can check the live version of the app here (Redux-vs-Recoil) and source code on Github. It’s just a matter of time Recoil becomes the new standard for managing shared states in your react app and is ready to be used in production applications. Until then give it a shot and follow the community to keep yourself updated with the new releases.

I suggest you to take a look at this 5 min quick egghead.io playlist by Tomasz Łakomy on Recoil to get started.

If you found this post helpful in understanding the basics of redux-vs-recoil give a 👏 and leave a comment on — What features would you love to see in Recoil? I’d love to hear any thoughts on this topic 😋.

Checkout my other stories and know more about me on 👉 chandan.dev. 👈 P.S: I built this site on Next.js 🚀.

结论:

所以,这是一个简单的增量计数器应用程序的 recoil 和 redux 之间的快速比较。你可以在这里查看应用程序的实时版本(redux -vs- recoil)和 Github 上的源代码。Recoil 将成为 react 应用程序中管理共享状态的新标准,并准备在生产应用程序中使用,这只是时间问题。在此之前,请尝试一下,并跟随社区,让自己保持与新版本的更新。

我建议你看看这个5分钟的视频。由 Tomasz Łakomy 开始。

如果你发现这篇文章对理解 redux vs recoil 的基础知识有帮助,请给👏留言-你想看到 recoil 的哪些特性?我很乐意听到关于这个话题的任何想法😋。

请登录👉chandan.dev查看我的其他故事,了解更多关于我的信息。👈P.S:我用 Next.js 建立了这个网站🚀。

译者注:计数器的例子有些过于简单,没有展示出 recoil 对异步逻辑的处理方式,有兴趣的可以在 recoil 的官网查看

译者的理解

recoil 的出现,主要是为了解决传统共享状态的问题。

  • 使用状态提升的话,部分状态的变化会导致整个组件树重新渲染

  • 使用 context

  • 不知道会有写多少个 context

  • 不利于代码的拆分,这里偏向于分行的观点

优点:

  • 足够独立的状态单元 atoms
  • 灵活的状态衍生 selectors
  • 易于拆分代码
  • 可以兼容 react 的并发模式,和 react 团队一起协商过解法

缺点:

  • 不像 redux 那么易于测试
  • selector 的作用不明确,既承担了衍生数据的作用,用有一部分处理 effects 的定位

总的来说,很看好这个库,因为可以无缝对接 react,而且处理了诟病已久的冗余渲染,试验阶段后的登场让人期待。