React Native 硬件交互指南:写给新手的 Context 与单例模式解析(小白版)

30 阅读5分钟

React Native 硬件交互指南:写给新手的 Context 与单例模式解析(小白版)

你好!如果听到“Context”、“单例”、“鲁棒性”这些词让你觉得头大,别担心。这篇文章就是专门为你准备的。我们将通过通俗易懂的比喻,拆解我们在 React Native 项目中管理硬件(如 PDA 扫码头、RFID 读卡器、蓝牙打印机)时用到的设计模式。


1. 基础概念:React 是如何工作的?

在深入硬件之前,我们需要先达成一个共识:React 的核心任务是“同步”

1.1 状态 (State) = 数据的“快照”

想象你的 App 界面是一个显示屏。React 不会一直盯着屏幕看,它只盯着一份数据,这份数据就叫 State

  • 当 State 发生变化(比如 count 从 0 变成 1),React 就会说:“哎哟,数据变了,我得把屏幕重新画一遍(Re-render)。”
  • 关键点:如果数据变了,但没有告诉 React(没有更新 State),屏幕是不会变的。

1.2 Context = 全局的大喇叭

通常,数据像接力棒一样,从父组件传给子组件,再传给孙子组件。但如果有很多层,或者毫无关系的两个组件都需要同一个数据(比如“当前登录用户”),接力棒就太累了。

  • Context 就像一个安装在屋顶的大喇叭
  • 只要你在屋顶喊一声(更新 Context),屋子里所有想听的人(使用 useContext 的组件),不管在哪个房间,都能立刻听到,并且根据新消息做出反应(重新渲染)。

1.3 单例 (Singleton) = 唯一的办事员

单例模式跟 React 没关系,它是纯 JavaScript 的概念。

  • 想象有一个办事员叫“扫码经理”。
  • 不管你在 App 的哪个页面叫他,来的永远是同一个人。
  • 他手里拿个小本本,记着谁在找他。他很高效,但有个缺点:他是个哑巴,只会干活,不会喊话。他干完活(扫码成功),除非你自己凑过去看他的本子,否则 React 不知道数据变了,界面也不会刷新。

2. 我们的挑战:硬件管理的三种流派

在我们的项目中,有三种硬件:红外扫码 (Scan)RFID 读卡蓝牙打印机。我们来看看为什么要用不同的方式管理它们。

2.1 混合模式:Scan 和 RFID 的“双保险”策略

场景: 我们需要在任何页面都能扫码(功能需求),同时也想在某个页面显示“扫码历史记录”(UI 需求)。

设计思路:结合 单例(干活) + Context(喊话)

原理拆解:
  1. 底层单例(干活的): 我们创建了一个 PhysicalKeyScanManager。它是一个单例对象。

    • 它的工作很简单:监听硬件按键 -> 收到数据 -> 告诉所有注册监听的人。
    • 它不依赖 React,只要 App 活着它就活着。
  2. 上层 Context(喊话的): 我们创建了一个 ScanProvider 包裹在 App 最外层。

    • 它监听底层单例的数据。
    • 一旦单例说“扫到了!”,Provider 就把这个数据存进自己的 useState 里。
    • 这一存,React 就知道了,于是通过 Context 这个大喇叭广播出去:“有新扫码数据啦!”
    • 于是,所有订阅了数据的页面(比如历史记录页)就会自动刷新。
什么是“鲁棒性控制”(回退机制)?

鲁棒性 (Robustness) 就是“皮实”、“抗造”。

想象一下,如果有一天,你写了一个新页面,但忘记把这个页面放在 ScanProvider(大喇叭)覆盖的范围内了。

  • 没有回退机制的代码: 你调用 useScan(),它试图找大喇叭,结果没找到,直接报错崩溃(Crash)。
  • 有回退机制的代码(我们现在的做法): 你调用 useScan(),它发现没找到大喇叭。 它心里想:“虽然没法广播更新 UI 了,但我可以直接把那个**办事员(单例)**介绍给你。” 于是,你的页面虽然拿不到“历史记录”,但依然可以找办事员注册监听,扫码功能依然可用!

这就是为什么我们在代码里写:

if (!context) {
  // 没找到 Context?没关系,给你返回基础功能
  return { scanManager: physicalKeyScanManager };
}

这对新手非常友好,因为你不需要背诵复杂的配置规则,拿来就能用。

2.2 纯 Context 模式:打印机的“严防死守”

场景: 蓝牙打印机连接是一个强交互的过程。点击连接 -> 转圈圈 -> 变绿显示“已连接”。

设计思路只用 Context,不提供回退。

为什么打印机不能像扫码那样“将就”? 因为打印机的核心价值在于状态同步

  • 如果连接成功了,但 UI 没变绿(因为没用 Context 驱动 React 更新),用户会以为没连上,疯狂点击,导致程序出错。
  • 我们需要确保:只要状态变了,UI 必须变
  • 所以,我们强制要求:必须在 Context 环境下使用。如果你忘记包裹 Provider,我就直接报错提醒你,而不是让你“带病工作”。
if (!context) {
  // 严厉的报错:必须在 Provider 里用!
  throw new Error('必须在 PrinterProvider 里使用!');
}

3. 总结:给新手的建议

作为新手,你在写 React Native 代码时,可以遵循这几个简单的原则:

  1. 分层思考

    • 脏活累活(硬件通信、API请求):写成普通的 JS 类或函数(单例),不要写在组件里。这样逻辑清晰,哪里都能用。
    • 界面展示(UI、状态):用 React 组件和 Hooks。
  2. 什么时候用 Context?

    • 当一个数据(比如用户信息、主题颜色、硬件连接状态)需要在很多个毫不相干的页面里使用时,用 Context。
    • 如果只是父传子,用 Props 就够了,别杀鸡用牛刀。
  3. 拥抱“回退机制”

    • 在你写自定义 Hook(比如 useMyFeature)时,多想一步:如果用户忘了配置环境,我的代码能不崩吗?能不能给个默认值?
    • 代码不崩,就是高手。

希望这篇文章能帮你理解这些看似高大上的概念。其实代码的设计模式,归根结底都是为了让我们干活更轻松、改bug更少