![cdf5548c3ddee61ebeb873571ae33e42.png](https://img-blog.csdnimg.cn/img_convert/cdf5548c3ddee61ebeb873571ae33e42.png)
本文最初发布于 Medium 网站,经原作者授权由 InfoQ 中文站翻译并分享。
Redux 的工作原理很简单:将应用程序的整个状态存放在一个中央存储中。所有组件都可以访问这一存储,因此无需在组件之间传递参数和属性。Redux 的主要构建块包括动作(Action)、Reducer 和存储(Store)。
先来看一下 Redux 是如何工作的,以便更好地了解它与替代方案之间的区别。
简而言之,来自组件的 API 调用将发送到 Reducer。它们是根据对象的旧状态返回新状态的函数。新状态存放在存储中,存储是 Redux 应用程序中访问先前状态的中央实体。
除了保持应用程序状态并控制对它的访问之外,存储还允许状态更新,并通过订阅来处理侦听器的注册和注销操作。
我们为什么需要 Redux 的替代品?Redux 看起来简单易用,但它也存在一些缺点:
学习曲线不算平缓。Redux 学起来可能并不容易,因为用户必须具备函数式编程知识。
简单的更改也需要较多样板代码。由于 Redux 中的流(flow)是预定义的,因此你必须用它。在较小的应用程序中,样板文件的数量相对来说就更显眼了。
Redux 中即使是很小的更改也会触发 DOM 重组过程。开发人员不喜欢这样,因为它很耗时,并且会对性能产生不利影响。
有很多状态管理库可以克服 Redux 的缺陷。它们也都有各自的优缺点。下面我们就来看看一些最流行的选项,并探讨它们最适合什么场景。
MobX 应用了响应式编程的概念来同步模型和 UI。它只会更新 UI 中需要更新的部分,而不会完全重新加载它。MobX 基于面向对象的概念和应用程序数据模型。MobX 简化了类和存储的文档,其中包括应用中使用的属性和方法。它能在开发过程中快速交付 UI 对象以实现快速开发,并允许添加生命周期 hook“componentWillReact()”以进行响应式更新。这简化了应用程序的路由和导航工作。
由于 MobX 是基于类的,因此对象的序列化并不像 JavaScript 那样简单。建议开发人员编写自定义的序列化和反序列化方法。MobX 要求数据通过 API 调用来序列化。
使用 MobX 时,建议为撤消 / 重做创建自定义函数,并建议对数据更改使用 time travel。以下是 MobX 的主要特性:
它可扩展、易于使用且不允许正规化数据。
它映射状态和派生状态之间的关系,同时保持引用完整性,从而减少错误数量。
它构建了一个虚拟派生图来尽量减少保持派生状态与状态同步所需的重计算次数,从而简化了状态修改过程。
它是一个 JavaScript 库,因此你可以保留已有的 JavaScript 工具。
它可以用在客户端和服务端。
import React from "react";
import { render } from "react-dom";
import { observable, action } from "mobx";
import { observer } from "mobx-react";
class LogTime {
@observable timekeeper = 0;
constructor() {
setInterval(() => {
this.timekeeper += 1;
}, 1000);
}
@action.bound
reset() {
this.timekeeper = 0;
}
}
const TimerViewer = observer(({ appState }) => (
<label onClick={appState.reset}>Seconds passed: {appState.timekeeper}label>
));
render(
<div>
<TimerViewer appState={new LogTime()} />
div>,
document.getElementById("root")
);
由于其透明的响应式编程特性,MobX 被广泛用作 Redux 的替代品。
https://mobx.js.org/README.html
Apollo GraphQLApollo GraphQL 用数据图层将现代应用与云连接起来。现代化的互联系统需要获取大量数据,但实际做起来并不容易。GraphQL 允许组件声明数据,从而满足了这一需求。它强大的端到端类型功能可提高服务器与应用程序之间的性能。
这一平台在一个中心化的位置上管理数据。你可以简单地连接平台或微服务,并将这些系统与必要的数据连接起来。GraphQL 负责数据获取和数据转换工作。它创建了一个抽象层,从而帮助解耦服务。GraphQL 是独立于平台和语言的。
可以使用 Apollo Boost 入门工具包来集成 Apollo GraphQL。你可以使用这个工具包配置客户端。它包括身份验证、内存缓存、状态管理和错误处理工具。Apollo GraphQL 还提供了自定义 Apollo Boost 所需的文档。
下面来创建一个 Apollo 客户端的示例。对于 Apollo 客户端来说,你需要 GraphQL 的端点。例如:https://48p1r2roz4.sse.codesandbox.io,这是 Apollo 提供的 playground。import ApolloClient from "apollo-boost";
const myApolloClient = new ApolloClient({
uri: `https://48p1r2roz4.sse.codesandbox.io`
});
接下来,将你的 Apollo Boost 连接到 React 客户端。
import React from "react";
import { render } from "react-dom";
import { ApolloProvider } from "react-apollo";
const App = () => (
Hook ApolloClient to React
);
render(, document.getElementById("root"));
Once the ApolloProvider is hooked, the data can be request using gal component of 'Apollo Boast' and Query component of 'React'.
query={gql`
{
// your query
}
}
`}
>
连接上查询组件后,服务器将在发送数据时自动对其进行缓存。针对自定义端点,Apollo Boost 提供了构建 Apollo 客户端所需的多个软件包。
https://www.apollographql.com/
Context API 和 React Hooks人们一直在结合使用 Hooks 和 Context API 来管理状态。
为什么要使用 hooks 和 context?我个人更喜欢在中小型应用中使用它们,因为它们让我能更容易地将可重用组件发布到 Bit 上。
未发布的组件也可以轻松在其他项目中重用,请记住这一点。
示例:浏览发布到 Bit.dev 的 React 组件
import React, { useState, createContext } from "react";
// Create Context Object
export const TokenContext = createContext();
// Create a provider for components to consume and subscribe to changes
export const TokenContextProvider = props => {
const [token, setToken] = useState(0);
return (
<TokenContext.Provider value={[token, setToken]}>
{props.children}
TokenContext.Provider>
);
};
现在,我们创建一个类来显示令牌。
import React, { useState, createContext } from "react";
// Create Context Object
export const TokenContext = createContext();
// Create a provider for components to consume and subscribe to changes
export const TokenContextProvider = props => {
const [token, setToken] = useState(0);
return (
<TokenContext.Provider value={[token, setToken]}>
{props.children}
TokenContext.Provider>
);
};
下一步是创建用于增加和减少令牌的按钮。我们创建 TokenButton.js 文件。
import React, { useContext } from "react";
import { Button } from "semantic-ui-react";
import { TokenContext } from "../context/TokenContext";
export default function tokenButtons() {
const [count, setCount] = useContext(TokenContext);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return (
<div>
<Button.Group>
<Button color="blue" onClick={increment}>
Increment
Button>
<Button color="orange" onClick={decrement}>
Decrement
Button>
Button.Group>
div>
);
}
创建一个 display 类来设置 UI。
import React, { useContext } from "react";
import { Statistic } from "semantic-ui-react";
import { TokenContext } from "../context/TokenContext";
export default function TokenDisplay() {
const [token] = useContext(TokenContext);
return (
<Statistic>
<Statistic.Value>{token}Statistic.Value>
Statistic>
);
}
现在,我们将创建一个名为 TokenView.js 的容器,以允许访问 userContext hook。在视图文件夹中创建一个 view 类,如下所示:
import React from "react";
import { Segment } from "semantic-ui-react";
import { TokenContextProvider } from "../context/TokenContext";
import TokenDisplay from "../components/TokenDisplay";
import TokenButtons from "../components/TokenButtons";
export default function TokenView() {
return (
<TokenContextProvider>
<h3>Tokenh3>
<Segment textAlign="center">
<TokenDisplay />
<TokenButtons />
Segment>
TokenContextProvider>
);
}
现在将视图文件导入 App.js。
import React from "react";
import "./styles.css";
import TokenView from "./views/TokenView";
import { Container } from "semantic-ui-react";
export default function App() {
return (
<div>
<Container>
<TokenView />
Container>
div>
);
}
它将输出以下结果。
https://gfycat.com/amusinghealthyarchaeocete
PullstatePullState 的工作方式与 Redux 类似,但是不支持类。它使用 hooks 进行全局状态管理。可以使用 hooks 启动多个存储,并使用“useState”从状态访问值。你可以创建一个存储,并使用“userState()”hook 使用存储的状态。我们使用这个 hook 来确保使用的状态是组件需要的。
https://lostpebble.github.io/pullstate/
下面是使用 PullState 更改样式属性的示例。首先,为你的主题创建一个存储。import { Store } from "pullstate";
export const UIThemeStore = new Store({
isRedTheme: true
});
接下来,我们将读取状态并在单击按钮时切换状态。
import React from "react";
import "./styles.css";
import { UIThemeStore } from "./UIStore";
export default function App() {
const isRedTheme = UIThemeStore.useState(s => s.isRedTheme);
return (
<divstyle={{background: isRedTheme ? "red" : "yellow",color: isRedTheme ? "yellow" : "red"
}}
>
<h1>Toggle Modes using Pull Stateh1>
<buttononClick={() =>
UIThemeStore.update(s => {
s.isRedTheme = !isRedTheme;
})
}
>
Toggle Red theme
button>
div>
);
}
RxJs
RxJS 是一个响应式扩展 JS 库(RxJS),它使用 Observables 进行异步调用。它基于事件,并能与 Redux 结合使用。RxJS 为应用程序提供了更好的性能和模块化水平,显示了更好的可调试调用堆栈,并具有向后兼容性。RxJS 中最常用的概念是 Observable、Scheduler 和 Subject。
https://github.com/ReactiveX/rxjs
下面是 RxJS 的一个简单的自定义 hooks 的代码段,其中:
使用“useEffect”hook 订阅一个 Observable
设置更改的状态
- 清理函数
const setObservable = observable => {
const [state, setState] = useState();
useEffect(() => {
const sub = observable.subscribe(setState);
return () => sub.unsubscribe();
}, [observable]);
return state;
};
小结
我们研究了 Redux 的一些最常用的替代方案,它们可以独立工作或在 React 应用程序中与 redux 交互。MobX 和 Apollo GraphQL 是其中最流行的,它们分别因为出色的性能和连接多个不同平台的能力而备受青睐。
还有不少人喜欢 Reactive Extension JS(RxJS)库,因为它使用了 Hooks 来代替 Redux。所以到头来,适合你项目的方案才是最好的选择。
延伸阅读https://blog.bitsrc.io/redux-react-alternatives-c1733793a339