You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Expo + @legendapp/state:离线多用户仅单个同步异常

本地优先React Native应用同步问题:离线添加多用户仅单个同步,重启后全部成功

问题场景

基于React Native + Expo开发本地优先应用,使用@legendapp/state管理状态、MMKV实现离线持久化。离线时点击「Add Random User」按钮添加多个用户,恢复联网后仅单个用户同步到服务器,其余用户未完成同步;但重启应用后,所有待同步用户均能成功提交到API服务器。

问题根源

  1. 变更跟踪逻辑缺陷:当前同步逻辑依赖changes参数筛选新用户(!change.prevAtPath),但离线批量添加用户后,恢复联网时的changes可能仅包含最后一次操作的变更,无法识别所有待同步用户。
  2. 缺少唯一标识:离线添加的用户无本地唯一ID,导致@legendapp/state无法准确跟踪每个待同步项,日志中出现的Multiple elements in array have the same ID警告印证了这一点。
  3. 重试机制未正确触发:虽设置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 08:15:22