React Native (Expo Go) 中的 Supabase "TypeError: blobId of undefined" 报错

状态
Published
Tags
Log
Tech_Tag
React_Native
Supabase
expo
Created
May 6, 2025 04:35 PM
关键词:supabase, react native, expo go
notion image
 
 
大家好!最近在我的 React Native (Expo Go) 项目中使用 Supabase 时,碰到了一个让人头疼的 bug,想把解决过程分享出来,希望能帮到正在跳坑的你。

问题表现:

我的"HoneyMoney-V2"应用出现了这些奇怪的现象:
  • 数据存储没问题:新交易能成功保存到 Supabase 数据库。在 Supabase 后台能看得到。
  • 成功提示没了:数据存好了,但应用就是不显示任何成功消息。
  • 数据显示挂了:已有数据完全加载不出来,页面上啥也看不到。
  • 控制台疯狂报错:一直弹出 TypeError: Cannot read property 'blobId' of undefined。这个错误总是在 Promise 解析时冒出来,藏在 Supabase 内部逻辑深处。

第一轮分析和 Blob 的坑:

看到 blobId of undefined 这个错误,我立马怀疑是 React Native 处理 Blob 对象的方式有问题,或者 Supabase 对这些对象的处理方式不对劲。我一开始尝试了这些方法:
  • 自己写个 Fetch 包装器:在 useSupabase.js 钩子里实现自定义 fetch 方案。想法是拦截 Supabase 的网络请求,手动构建返回对象,试图绕过那个出问题的 blobId。这需要临时替换全局的 fetch 函数。
  • 调整 Supabase 客户端配置:简化和微调 supabase.js 客户端设置,尽量让它对 React Native 更友好。
  • 塞满各种 Polyfills:我已经用上了 react-native-polyfill-globals 和其他补丁,比如 react-native-get-random-values 和 react-native-url-polyfill。我甚至调整了它们在 polyfills.js 中的加载顺序,还额外添加了 text-encoding。
这些尝试看起来很合理,毕竟 Supabase SDK 主要是为浏览器环境设计的,那里有现成的 fetch、Blob、FormData、URL 和 Crypto 等 API。而 React Native 虽然强大,但它的 JavaScript 运行时(比如 Hermes)和浏览器环境有不少差异。所以,polyfills 往往是必不可少的。

Expo Go 的特殊性与踩到的坑:

关于 React Native 中 Blob 问题的常见解决方案是使用 rn-fetch-blob 或它的维护分支 react-native-blob-util。我顺着这个思路,加了 react-native-blob-util 并尝试用它来全局补充 Blob:
// 在入口文件 (App.js 或 index.js) 里 - 结果在 EXPO GO 中报错了 import { Blob } from 'react-native-blob-util'; global.Blob = Blob;
这时情况急转直下。虽然 react-native-blob-util 对可以链接原生模块的项目(纯 React Native 项目或 Expo 开发构建版)效果不错,但在我的 Expo Go 环境中碰壁了。报错信息很明确:
ERROR Invariant Violation: Your JavaScript code tried to access a native module that doesn't exist. If you're trying to use a module that is not supported in Expo Go, you need to create a development build of your app.
这个信息给了我关键提示:Expo Go 不能用需要原生模块的库,而 react-native-blob-util 就是这样的库。

重新审视问题本质:

结合 blobId of undefined 错误和 Expo Go 的原生模块限制,我终于看清了问题本质:Supabase(或它依赖的像 postgrest-js 这样的库)期望一个带有特定属性或方法的 Blob 对象。当它在 React Native(Expo Go)环境中找不到完全匹配的 Blob 实现,或者当它收到的不是预期对象(可能在某些内部处理中变成了 undefined),它就在尝试访问 blobId 时崩溃了。
问题不是我的数据本身是 Blob,而是 Supabase 客户端在处理响应或内部运作时,可能需要用到在 Expo Go 沙盒环境中不完整或根本不存在的 Blob 相关功能。

真正管用的解决方案(Expo Go 适用):blob-polyfill

既然 react-native-blob-util 因为依赖原生组件而不能用,我需要一个纯 JavaScript 的 Blob polyfill。答案是:blob-polyfill。
下面是最终解决 TypeError: blobId of undefined 的步骤:
  1. 先卸载那个依赖原生模块的库(如果之前装过):
npm uninstall react-native-blob-util # 或者 yarn remove react-native-blob-util
  1. 安装 blob-polyfill:
npm install blob-polyfill # 或者 yarn add blob-polyfill
  1. 更新入口文件(比如 App.js 或 index.js): 关键是要在入口文件最顶部导入这些 polyfills,一定要在其他所有导入之前(特别是在 React、App 组件或 Supabase 客户端初始化之前)。
// App.js 或 index.js 的最顶部 // Supabase 必备(用于 UUID 生成等) import 'react-native-get-random-values'; // Supabase 必备(URL 解析用) import 'react-native-url-polyfill/auto'; // 关键:专为 Expo Go 准备的纯 JavaScript Blob polyfill import { Blob } from 'blob-polyfill'; // 如果 Supabase 需要 TextEncoder/TextDecoder(通常需要) import { TextEncoder, TextDecoder } from 'text-encoding'; global.Blob = Blob; global.TextEncoder = TextEncoder; global.TextDecoder = TextDecoder; // --- 这之后才是你的其他导入 --- import React from 'react'; import { AppRegistry } from 'react-native'; import App from './src/App'; // 或你的主应用组件 import { name as appName } from './app.json'; // ... 入口文件的其他内容 AppRegistry.registerComponent(appName, () => App);

二、blob-polyfill 的解决方案原理

1. 核心思想:JavaScript 实现 Blob
// blob-polyfill 的核心代码简化示例 class Blob { constructor(parts = [], options = {}) { this._buffer = Buffer.concat(parts.map(p => Buffer.from(p))); this.type = options.type || ''; } size() { return this._buffer.length } slice(...) { ... } // 实现切片方法 }
  • 纯 JS 实现:通过 BufferUint8Array 模拟二进制数据
  • API 兼容性:实现浏览器标准的 Blob 构造函数和方法
2. 与原生模块的本质区别
特性
blob-polyfill
react-native-blob-util
实现方式
纯 JavaScript
需要原生模块(Java/Objective-C)
性能
较低(内存操作)
较高(直接操作文件系统)
适用场景
小文件处理
大文件/流处理
Expo Go 兼容性
✅ 支持
❌ 需要开发构建

为什么这招管用:

  • blob-polyfill:提供了纯 JavaScript 实现的 Blob 接口。把它赋值给 global.Blob 后,就有了一个符合标准的全局 Blob 构造函数。这样 Supabase 就能用这个构造函数和 Blob 实例交互,不会碰到 undefined 的 blobId 属性。
  • react-native-get-random-values:Supabase 依赖全局 crypto API 做 UUID 生成等操作。这个补丁提供了必要的 crypto.getRandomValues。
  • react-native-url-polyfill/auto:Supabase 需要处理 URL。这个补丁确保有符合标准的 URL 和 URLSearchParams API。
  • text-encoding:Supabase 可能用 TextEncoder 或 TextDecoder 处理请求/响应体。
  • 加载顺序:把这些放在最顶部确保在 Supabase 客户端初始化或发网络请求前,必要的全局浏览器 API 已经准备好了。
 
notion image

经验总结:

  • 环境差异很关键:React Native(尤其是 Expo Go)不是完整的浏览器环境。Polyfills 是你的好帮手。
  • Expo Go 有限制:记住,Expo Go 用不了带原生代码的库。如果非要用这类库,就得创建开发构建版本。
  • 优先选纯 JS 方案:对 Expo Go 来说,首选纯 JavaScript polyfills(比如用 blob-polyfill 而非 react-native-blob-util)。
  • 加载顺序至关重要:Polyfills 一般要在依赖它们的代码之前初始化。入口文件顶部是最安全的位置。
  • "blobId" 其实是个表象:虽然错误提到了 blobId,但根本问题是 Supabase 在内部处理响应时,缺少了它期望的、正常工作的 Blob 对象/API。
实施了 blob-polyfill 方案并确保其他必要的 polyfills 正确加载后,TypeError: blobId of undefined 终于消失了!数据开始正常显示,成功提示也如期而至。
希望这份详细记录能为你省下不少摸索时间!祝编码顺利!