无论React是否是你最喜欢的JavaScript框架,它都_是_ 最常用的Web框架。如果你正在构建单页应用、高保真前台,甚至在某些情况下是静态网站,那么你很有可能在使用React来做。
在过去,有时很难决定把来自库的代码放在最佳位置,比如ably-js,它可能会导致重新渲染,而且它有自己的状态管理。功能性组件和React Hooks使这种选择变得简单多了。我们已经建立了一个Ably React Hooks npm包,以减少你的心理负担,使Ably与React的使用变得轻而易举。
React的状态管理
传统上React的工作方式是将组件绑定到组件之间使用"props"传递的状态上。Props是传递给React组件构造器的参数。React组件就像JavaScript函数一样,它们接受props并返回一个要渲染的React元素。这种 "道具传递 "模式通常由Redux等状态管理库来补充,以集中控制应用程序的状态。此外,应用程序经常包括 "有状态 "的组件,这些组件创建、维护和更新自己的状态--通常是通过API请求来捕获初始状态,并将任何变化反映到他们的数据来源的API上。
有状态的组件通常是通过使用React的componentDidMount() 方法实现逻辑来编写的。这允许我们在组件被渲染时执行代码。这对于简单的HTTP调用来说效果很好,但当与实现其自身状态管理的代码相结合时,就会产生问题。
使用componentDidMount和Ably时发生的问题
我们从使用Ably的JavaScript库(ably-js)和React的人那里得到的最常见的问题之一是他们应该在哪里管理Ably客户端实例的生命周期。他们经常发现,通过做看似明显的技术--创建一个客户端,并在componentDidMount 中订阅消息--他们最终迅速耗尽了连接限制并达到了他们的消息配额。这是因为ably-js维护着它自己的连接和订阅列表,而每次组件挂载时的订阅会增加一个额外的订阅。更糟糕的是,如果React UI根据Ably消息改变并重新渲染(通常是这样),就有可能出现递归的情况,即每次收到消息,组件都会重新渲染并创建一个额外的订阅,并成倍地增加Ably连接的使用。
Ably DevRel和客户支持部门花了很多时间为开发者提供指导,以确保他们的ably-js客户端实例得到正确的管理,并且在componentDidUnmount 期间取消频道订阅。这种React方法允许我们在组件被移除时运行代码(这发生在更改时重新渲染组件之前),而ably-js客户端实例本身是长效的。我们甚至有一个GitHub的React组件示例库来提供这种确切模式的例子,并且在过去制作了基础组件来使其更容易。
React功能组件来拯救...?
在2019年(自16.8版本发布以来),React引入了功能组件的概念(与基于类或 "经典 "组件形成对比)。一目了然,功能组件比经典组件更简单,它们不再需要componentDidMount 和componentDidUnmount ,而是倾向于使用React Hooks来管理状态。
在较新的代码库中,功能型组件通常更受欢迎,因为它们在视觉上很简单,而且与开发人员经常倡导的功能型编程理想更接近。然而,从函数式组件中制作有状态的组件有点棘手,需要使用ReactuseState 、useEffect 和useMemo 钩子来产生类似于将代码放入componentDidMount 和componentDidUnmount 的结果。不幸的是,这意味着在哪里创建Ably客户端的实例,以及何时订阅和取消订阅频道的困惑也许会被功能组件放大。
进入自定义React Hooks
为了解决这个困惑,我们已经建立了几个自定义的React Hooks,用于在React功能组件中使用Ably。我们还把它们作为一个npm包发布,这样你就可以把它们直接导入你的React项目中。
它们提供了一个使用Ably客户端的防弹模式。它们允许你在你的组件收到消息时得到一个回调,如果你订阅的通道被修改,它们会重新渲染和重新订阅,它们使存在事件变得简单易行,这样你的React应用就可以知道其他连接的客户端。
使用Ably和React Hooks包订阅一个频道的顺序图
使用Ably和React Hooks包发布消息的顺序图
使用React Hooks包从Ably接收消息的顺序图。
我如何使用npm包?
安装
这些React Hooks以ES6模块的形式发货,所以你可以在你的react代码中使用导入语法。
npm install --save @ably-labs/react-hooks
这在使用create-react-app时是开箱即用的 - 所以你可以立即使用该包。
Ably通道和API密钥
为了使用这些钩子,你将需要一个Ably API密钥。如果你还没有注册,你可以现在注册一个免费的Aply账户。一旦你有了Ably账户。
- 登录到你的应用程序仪表板。
- 在 "你的应用程序 "下,点击你希望在本教程中使用的任何应用程序的 "管理应用程序",或用 "创建新应用程序 "按钮创建一个新的应用程序。
- 点击 "API密钥 "标签。
- 从你的根密钥中复制秘密的 "API密钥 "值,我们稍后在建立我们的应用程序时将使用它。
我们强烈建议你使用令牌认证,以验证Ably服务。在下面的例子中,我们在标记中直接使用API密钥,这只适用于本地开发,不应该用于生产代码,也不应该提交到你的存储库。
使用方法
一旦你用npm将包添加到你的项目中,你就可以在你的React代码中使用钩子。首先,添加一个对钩子的引用。
import { configureAbly, useChannel } from "@ably-labs/react-hooks";
然后你需要使用configureAbly 函数来创建一个Ably JavaScript SDK的实例。
configureAbly({ key: "your-ably-api-key", clientId: generateRandomId() });
configureAbly 与Ably SDK的方法签名相匹配--并且需要一个字符串或一个AblyClientOptions 。你可以使用这个配置对象来设置你的API密钥,或者像通常那样使用tokenAuthentication 。如果你想使用usePresence 钩子,你需要明确地提供一个clientId 。
一旦你完成了这些,你就可以在你的代码中使用钩子。最简单的例子是这样的。
const [channel] = useChannel("your-channel-name", (message) => {
console.log(message);
});
每次有消息被发送到your-channel-name ,它就会被记录到控制台。你可以对这些消息做任何你需要的事情。
useChannel()
useChannel 钩子让你订阅一个频道并从它那里接收消息。
const [channel, ably] = useChannel("your-channel-name", (message) => {
console.log(message);
});
通道实例和Aply JavaScript SDK实例都会从useChannel 调用中返回。
useChannel 与常规的reactuseState 钩子结合起来才会真正发挥其作用--例如,你可以在你的应用状态中保留一个消息列表,并使用useChannel 钩子来订阅一个频道,并在新消息到达时更新状态。
const [messages, updateMessages] = useState([]);
const [channel] = useChannel("your-channel-name", (message) => {
updateMessages((prev) => [...prev, message]);
});
// Convert the messages to list items to render in a react component
const messagePreviews = messages.map((msg, index) => {
<li key={index}>{msg.data.someProperty}</li>
});
useChannel 支持常规调用channel.subscribe 的所有参数组合,这意味着你可以通过向useChannel 函数提供一个消息类型来过滤你订阅的消息。
const [channel] = useChannel("your-channel-name", "test-message", (message) => {
console.log(message);
// Only logs messages sent using the `test-message` message type
});
由useChannel 返回的通道实例可以用来向通道发送消息。它只是一个普通的Aply JavaScript SDK通道实例。
channel.publish("test-message", { text: "message text" });
usePresence()
usePresence 钩子可以让你订阅一个频道上的存在事件 - 这将在用户加入或离开频道时通知你。
const [presenceData, updateStatus] = usePresence("your-channel-name");
// Convert presence data to list items to render
const peers = presenceData.map((msg, index) => {
<li key={index}>{msg.clientId}: {msg.data}</li>
});
usePresence 返回一个存在信息的数组(每个信息是一个普通的Ably JavaScript SDK presenceMessage实例)。当你使用usePresence 来设置一个初始存在数据字符串时,你可以选择提供一个字符串。
const [presenceData, updateStatus] = usePresence("your-channel-name", "initial state");
updateStatus 函数可用于更新当前客户端的存在数据。
updateStatus("new status");
新的状态将被发送到信道,任何订阅了该信道的其他客户端都会被立即通知这一变化。
看到它的使用情况
我们最近在一个演示聊天应用程序中使用了这个包。这个功能齐全、可扩展的聊天应用是用React构建的,你可以看到聊天组件里面的 useChannel 钩子的例子。
让我们知道
我们希望这些钩子能让用Ably实时元素构建React应用程序变得轻而易举。如果你有任何问题或希望看到任何额外的功能,请在devrel@ably.com,或在Twitter上@ablyrealtime与我们联系。