React Native Expo + @legendapp/state:离线多用户仅单个同步异常
本地优先React Native应用同步问题:离线添加多用户仅单个同步,重启后全部成功
问题场景
基于React Native + Expo开发本地优先应用,使用@legendapp/state管理状态、MMKV实现离线持久化。离线时点击「Add Random User」按钮添加多个用户,恢复联网后仅单个用户同步到服务器,其余用户未完成同步;但重启应用后,所有待同步用户均能成功提交到API服务器。
问题根源
- 变更跟踪逻辑缺陷:当前同步逻辑依赖
changes参数筛选新用户(!change.prevAtPath),但离线批量添加用户后,恢复联网时的changes可能仅包含最后一次操作的变更,无法识别所有待同步用户。 - 缺少唯一标识:离线添加的用户无本地唯一ID,导致
@legendapp/state无法准确跟踪每个待同步项,日志中出现的Multiple elements in array have the same ID警告印证了这一点。 - 重试机制未正确触发:虽设置
retrySync: true,但同步失败后未正确标记待重试用户,仅在重启应用(重新读取持久化数据)时才会重新触发同步。
修复方案
1. 修改同步逻辑,直接读取本地待同步用户
不再依赖changes参数,从本地store中筛选未同步的用户(新增isSynced标记):
const store$ = observable({ users: synced({ initial: [], get: () => fetch("http://192.168.0.80:8080/api/users") .then((res) => res.json()) .then((data) => { // 标记服务器返回的用户为已同步 return data.content.map(user => ({...user, isSynced: true})); }), set: async ({ onError }) => { try { // 筛选未同步且无服务器ID的用户 const pendingUsers = store$.users.get().filter(user => !user.isSynced && !user.id); if (pendingUsers.length === 0) { console.log("No new users to sync."); return Promise.resolve(); } const syncPromises = pendingUsers.map(async (user) => { try { const response = await fetch( "http://192.168.0.80:8080/api/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ firstName: user.firstName, lastName: user.lastName, email: user.email, pictureUrl: user.pictureUrl }), } ); if (!response.ok) { throw new Error(`Failed to sync user. HTTP status: ${response.status}`); } const result = await response.json(); // 更新本地用户为已同步,替换为服务器ID const userIndex = store$.users.get().findIndex(u => u.localId === user.localId); if (userIndex !== -1) { store$.users[userIndex].set({ ...result, isSynced: true }); } console.log("User synced successfully:", result); } catch (error) { console.error("Error syncing user:", error.message || error); if (onError) { onError(error, { user, retryCount: 1, backoff: 2000 }); } } }); await Promise.all(syncPromises); return Promise.resolve(); } catch (err) { return Promise.reject(err); } }, changesSince: "last-sync", persist: { plugin: ObservablePersistMMKV, name: "messages", retrySync: true, }, retry: { infinite: true, maxDelay: 30, backoff: 'exponential', }, debounceSet: 500, }), });
2. 添加本地临时ID,唯一标识离线用户
修改添加用户逻辑,生成本地唯一ID并标记未同步:
const handleAddRandomUser = () => { const randomUser = { localId: Math.random().toString(36).substr(2, 9), // 本地临时唯一ID firstName: "Random", lastName: "User", email: `random.user${Math.floor(Math.random() * 100)}@example.com`, pictureUrl: "https://randomuser.me/api/portraits/men/67.jpg", isSynced: false // 标记为未同步 }; store$.users.push(randomUser); };
3. 优化FlatList的keyExtractor
使用本地ID或服务器ID作为唯一key,避免重复警告:
keyExtractor={(item) => item.id?.toString() || item.localId}
4. 完善重试机制
开启无限重试并使用指数退避,确保网络恢复后自动重试:
retry: { infinite: true, maxDelay: 30, backoff: 'exponential', }
内容的提问来源于stack exchange,提问作者sudeep cv
相关产品推荐
相关产品推荐

