React SDK下Realm Device Sync离线启动挂起问题求助
问题概述
按官方文档配置离线访问Realm后,App离线启动时仍强制发起网络连接,导致无限停在fallback视图。Realm包含两个Schema:一个单向同步(asymmetric sync)、一个双向同步(flexible sync),联网状态下同步服务正常,仅离线启动出现异常。
已尝试以下方案未解决:
- 清除App数据
- 联网登录后关闭App,离线重启
- 添加/移除
initialSubscriptions配置 - 配置超时后台同步方案
预期:离线时使用缓存用户与本地Realm正常启动App
实际:无限挂起在sync配置的fallback组件,控制台输出网络错误,且sync的onError回调未触发
依赖版本
"expo": "~48.0.21", "react": "18.2.0", "realm": "^12.11.1", "@realm/react": "^0.8.0"
核心代码
const RealmWrapper = ({children}) => { const [syncError, setSyncError] = useState(null); const realmAccessBehavior = { type: OpenRealmBehaviorType.OpenImmediately, }; return ( <AppProvider id={APP_ID} logLevel={'trace'} logger={(level, message) => console.log(`[${level}]: ${message}`)}> <UserProvider fallback={LoginWrapper}> <RealmProvider schema={[SurveyResults, SurveyDesign]} fallback={<RealmLoading />} sync={{ flexible: true, existingRealmBehavior: realmAccessBehavior, newRealmFileBehavior: realmAccessBehavior, onError: (_, error) => { console.log("I'm In the sync error handler!"); setSyncError(error); }, initialSubscriptions: { update(subs, realm) { subs.add(realm.objects(SurveyDesign).filtered("name != nil"), { name: "All Survey Designs", behavior: WaitForSync.Never, }); }, }, }} > {children} </RealmProvider> </UserProvider> </AppProvider> ); }
控制台错误日志
ERROR [Realm.Sync.Client.Network - error] Failed to resolve 'ws.services.cloud.mongodb.com:443': Host not found (authoritative) ERROR [Realm.App - error] App: request location failed (CustomError -1): request to https://...../location failed, reason: Network request failed ERROR [Realm.App - error] App: refresh_access_token: ...... -> 0 ERROR: request to https://....../location failed, reason: Network request failed
问题根源
从错误日志可见,离线启动时App正在尝试刷新access token,而UserProvider默认会在启动时验证缓存用户的会话有效性,强制发起联网请求刷新token。这一步卡住后,RealmProvider无法进入本地Realm加载流程,一直停在fallback视图。
即使配置了existingRealmBehavior: OpenImmediately,但用户会话验证的优先级更高,导致Realm的离线访问逻辑无法触发。
解决方案
1. 禁用UserProvider的离线自动token刷新
在UserProvider中添加sessionKeepAlive={false},让离线时直接使用缓存的用户会话,不再强制发起token刷新请求:
// 修改后的UserProvider配置 <UserProvider fallback={LoginWrapper} sessionKeepAlive={false}> {/* ... 其他代码 ... */} </UserProvider>
2. 确认initialSubscriptions的行为配置
确保initialSubscriptions中的behavior设置为WaitForSync.Never,避免等待同步完成才加载本地数据(你已配置此项,可保留)。
3. 额外的异常处理(可选)
若缓存用户的会话已过期,离线时无法刷新token,可在业务逻辑中添加判断,提示用户联网重新登录,同时保证已有的本地数据可以正常访问。
修改后的完整核心代码
const RealmWrapper = ({children}) => { const [syncError, setSyncError] = useState(null); const realmAccessBehavior = { type: OpenRealmBehaviorType.OpenImmediately, }; return ( <AppProvider id={APP_ID} logLevel={'trace'} logger={(level, message) => console.log(`[${level}]: ${message}`)}> {/* 禁用离线自动刷新token */} <UserProvider fallback={LoginWrapper} sessionKeepAlive={false}> <RealmProvider schema={[SurveyResults, SurveyDesign]} fallback={<RealmLoading />} sync={{ flexible: true, existingRealmBehavior: realmAccessBehavior, newRealmFileBehavior: realmAccessBehavior, onError: (_, error) => { console.log("I'm In the sync error handler!"); setSyncError(error); }, initialSubscriptions: { update(subs, realm) { subs.add(realm.objects(SurveyDesign).filtered("name != nil"), { name: "All Survey Designs", behavior: WaitForSync.Never, }); }, }, }} > {children} </RealmProvider> </UserProvider> </AppProvider> ); }
内容的提问来源于stack exchange,提问作者Austin

