变乱驱动架构:打造清楚的React组件通信
原文链接:Event-Driven Architecture for Clean React Component Communication
作者:NicolaLC
译者:倔强青铜三
前言
大家好,我是倔强青铜三。是一名热情的软件工程师,我热衷于分享和传播IT技术,致力于通过我的知识和技能推动技术交流与创新,接待关注我,微信公众号:倔强青铜三。接待点赞、收藏、关注,一键三连!!!
问题:Props Drilling和回调链
在现代应用开发中,组件间的状态管理和通信很快会变得繁琐。特别是在涉及Props Drilling的场景中——数据必须通过多层嵌套组件通报——以及回调链,这可能导致逻辑杂乱,使代码更难维护或调试。
这些挑衅常常导致组件紧密耦合,低落灵活性,并增加开发者追踪数据流经应用的心智负担。如果没有更好的方法,这种复杂性会显著拖慢开发速率,并导致脆弱的代码库。
传统流程:Props向下,回调向上
在典型的React应用中,父组件向下通报Props给子组件,子组件通过触发回调向上与父组件通信。这对于浅层组件树来说没问题,但随着层级的加深,事情开始变得杂乱:
Props Drilling:数据必须手动通过多个层级的组件通报,即使只有最深的组件需要它。
Callback Chains:同样,子组件必须将变乱处理器向上通报,创建紧密耦合且难以维护的结构。
常见问题:回调复杂性
以这个场景为例:
- 父组件向子组件A通报Props。
- 然后,Props被钻取到孙组件A/B,最终到达子孙组件N。
- 如果子孙组件N需要通知父组件一个变乱,它触发一个回调,该回调通过每个中心组件向上通报。
随着应用的增长,这种设置变得越来越难以管理。中心组件常常充当无用的中心人,转发Props和回调,这使代码膨胀,低落了可维护性。
为了解决Props Drilling问题,我们常常告急于全局状态管理库(比方,Zustand)来简化数据共享。但是,回调管理呢?
这就是变乱驱动方法可以改变游戏规则的地方。通过解耦组件并依赖变乱来处理交互,我们可以显著简化回调管理。让我们探索这种方法是如何工作的。
解决方案:变乱驱动方法
与依赖直接回调向上通信差别,变乱驱动架构解耦组件并集中通信。以下是它的工作原理:
变乱派发
当子孙组件N触发一个变乱(比方,onMyEvent),它不会直接在父组件中调用回调。
相反,它派发一个由集中的变乱处理器处理的变乱。
集中处理
变乱处理器监听派发的变乱并处理它。
它可以通知父组件(或任何其他感兴趣的组件)或触发所需的其他操纵。
Props保持向下
Props仍旧沿着层级向下通报,确保组件接收到它们需要的数据以正常工作。
这可以通过像zustand、redux这样的集中状态管理工具来解决,但本文不涉及。
实现
但是,我们如何实现这种架构呢?
useEvent钩子
让我们创建一个名为useEvent的自定义钩子,这个钩子将负责处理变乱订阅,并返回一个派发函数来触发目标变乱。
由于我使用的是TypeScript,我需要扩展窗口Event接口以创建自定义变乱:
- interface AppEvent<PayloadType = unknown> extends Event {
- detail: PayloadType;
- }
- export const useEvent = <PayloadType = unknown>(
- eventName: keyof CustomWindowEventMap,
- callback?: Dispatch<PayloadType> | VoidFunction
- ) => {
- ...
- };
复制代码 通过这样做,我们可以定义自定义变乱映射并通报自定义参数:
- interface AppEvent<PayloadType = unknown> extends Event {
- detail: PayloadType;
- }
- export interface CustomWindowEventMap extends WindowEventMap {
- /* 自定义事件 */
- onMyEvent: AppEvent<string>; // 一个带有字符串有效载荷的事件
- }
- export const useEvent = <PayloadType = unknown>(
- eventName: keyof CustomWindowEventMap,
- callback?: Dispatch<PayloadType> | VoidFunction
- ) => {
- ...
- };
复制代码 现在我们已经定义了所需的接口,让我们看看最终的钩子代码:
- import { useCallback, useEffect, type Dispatch } from "react";
- interface AppEvent<PayloadType = unknown> extends Event {
- detail: PayloadType;
- }
- export interface CustomWindowEventMap extends WindowEventMap {
- /* 自定义事件 */
- onMyEvent: AppEvent<string>;
- }
- export const useEvent = <PayloadType = unknown>(
- eventName: keyof CustomWindowEventMap,
- callback?: Dispatch<PayloadType> | VoidFunction
- ) => {
- useEffect(() => {
- if (!callback) {
- return;
- }
- const listener = ((event: AppEvent<PayloadType>) => {
- callback(event.detail); // 使用`event.detail`自定义有效载荷
- }) as EventListener;
- window.addEventListener(eventName, listener);
- return () => {
- window.removeEventListener(eventName, listener);
- };
- }, [callback, eventName]);
- const dispatch = useCallback(
- (detail: PayloadType) => {
- const event = new CustomEvent(eventName, { detail });
- window.dispatchEvent(event);
- },
- [eventName]
- );
- // 返回一个派发事件的函数
- return { dispatch };
- };
复制代码 useEvent钩子是一个自定义的React钩子,用于订阅和派发自定义窗口变乱。它答应您监听自定义变乱,并用特定有效载荷触发它们。
我们在这里做的非常简单,我们使用标准变乱管理体系并扩展它以适应我们的自定义变乱。
参数:
- eventName(字符串):要监听的变乱名称。
- callback(可选):当变乱被触发时调用的函数,接收变乱的detail(自定义有效载荷)作为参数。
特性:
- 变乱监听器:它监听指定的变乱,并用变乱的detail(自定义有效载荷)调用提供的callback。
- 派发变乱:钩子提供了一个dispatch函数,用自定义有效载荷触发变乱。
示例:
- const { dispatch } = useEvent("onMyEvent", (data) => console.log(data));
- // 派发事件
- dispatch("Hello, World!");
- // 派发时,事件将触发回调
复制代码 好的,但是关于
真实天下的例子?
查看这个StackBlitz例子:
https://stackblitz.com/edit/event-drive-arch?embed=1&file=src%2FApp.tsx&hideExplorer=1&hideNavigation=1
这个简单的例子展示了useEvent钩子的目标,基本上,body的按钮正在派发一个变乱,该变乱被Sidebar、Header和Footer组件拦截,并相应更新。
这让我们定义了因果反应,而无需将回调传播到很多组件。
注意
正如评论中指出的,请记住使用useCallback来记忆回调函数,以避免连续的变乱移除和创建,因为回调自己将是useEvent内部useEffect的依赖项。
真实天下的useEvent用例
以下是一些真实天下的用例,其中useEvent钩子可以简化通信并在React应用中解耦组件:
1. 通知体系
通知体系通常需要全局通信。
- 场景:
- 当API调用乐成时,需要在应用中显示“乐成”通知。
- 像头部的“通知徽章”这样的组件也需要更新。
- 解决方案:使用useEvent钩子派发带有通知详情的onNotification变乱。像NotificationBanner和Header这样的组件可以独立监听此变乱并更新。
2. 主题切换
当用户切换主题(比方,明/暗模式)时,可能需要多个组件相应。
- 场景:
- ThemeToggle组件派发自定义onThemeChange变乱。
- 像Sidebar和Header这样的组件监听此变乱并相应更新它们的样式。
- 利益:无需通过整个组件树通报主题状态或回调函数。
3. 全局按键绑定
实现全局快捷方式,比方按“Ctrl+S”生存草稿或按“Escape”关闭模态框。
- 场景:
- 全局keydown监听器派发带有按下键详情的onShortcutPressed变乱。
- 模态组件或其他UI元素相应特定快捷方式,而无需依赖父组件转发按键变乱。
4. 实时更新
像谈天应用或实时仪表板这样的应用需要多个组件对实时更新做出反应。
- 场景:
- WebSocket连接派发onNewMessage或onDataUpdate变乱,当新数据到达时。
- 像谈天窗口、通知和未读消息计数器这样的组件可以独立处理更新。
5. 跨组件表单验证
对于具有多个部门的复杂表单,验证变乱可以集中处理。
- 场景:
- 表单组件在用户填写字段时派发onFormValidate变乱。
- 择要组件监听这些变乱以显示验证错误,而无需与表单逻辑紧密耦合。
6. 分析跟踪
跟踪用户交互(比方,按钮点击,导航变乱)并将它们发送到分析服务。
- 场景:
- 派发带有相关详情(比方,点击按钮的标签)的onUserInteraction变乱。
- 一个中央分析处理器监听这些变乱并将它们发送到分析API。
7. 协作工具
对于像共享白板或文档编辑器这样的协作工具,变乱可以管理多用户交互。
- 场景:
- 每当用户绘制、打字或移动对象时,派发onUserAction变乱。
- 其他客户端和UI组件监听这些变乱以实时反映更改。
通过在这些场景中使用useEvent钩子,您可以创建模块化、可维护和可扩展的应用,而无需依赖深层嵌套的Props或回调链。
结论
变乱可以改变您构建React应用的方式,通过低落复杂性并提高模块化。从小处开始——确定您的应用中可以从解耦通信中受益的几个组件,并实现useEvent钩子。
通过这种方法,您不但简化了代码,还使其更易于维护和扩展。
为什么使用变乱?
当您的组件需要对应用步伐的其他部门发生的事情做出反应时,变乱会大放异彩,而无需引入不须要的依赖或复杂的回调链。这种方法淘汰了认知负担,并避免了组件紧密耦合的陷阱。
我的建议
当一个组件需要通知其他组件有关操纵或状态更改时,无论它们在组件树中的位置如何,都使用变乱举行组件间通信。
避免使用变乱举行组件内通信,特别是对于紧密相关或直接连接的组件。对于这些场景,请依赖React的内置机制,如Props、状态或上下文。
平衡的方法
虽然变乱功能强大,但过度使用它们可能会导致杂乱。明智地使用它们来简化松散连接的组件之间的通信,但不要让它们取代React的标准工具,以管理本地交互。
最后感谢阅读,接待关注我,微信公众号:倔强青铜三。
接待点赞、收藏、关注,一键三连!!!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |