React Native + Realm 离线方案处理

一、为什么选择 Realm?

  • 无需 SQL,直接用对象操作数据
  • 读写性能高,支持实时数据响应
  • 自带数据监听,数据变化 UI 自动刷新
  • 支持增量同步 / 增量下载,完美适配移动端离线业务
  • 跨平台(iOS/Android/React Native)一套代码通用

二、环境搭建

安装依赖  * Android 12+ / RN 0.71.3 / Realm 11.10.1

# React Native 项目
npm install realm @realm/react

三、操作案例:

需求:

仓库作业系统,离线操作,

物料辅计量单位等基础信息内容存储在本地,

页面中操作查询的时候查询本地基础数据支持离线操作

1. 定义模型: 物料档案、计量单位等

import Realm from 'realm';

// 实体模型
/**
 * 物料档案(MATERIAL)结构化存储
 * - primaryKey: code
 * 目标:可通过 code 快速取到 wholemanaflag / qualityunit 等字段
 */
export class Material extends Realm.Object<Material> {
    code!: string;
    pk_group!: string;
    pk_org!: string;
    pk_material!: string;
    pk_marbasclass!: string;

    name!: string;
    materialshortname!: string;
    materialtype!: string;
    materialbarcode!: string;
    materialspec!: string;
    graphid!: string;

    pk_measdoc!: string;
    pk_marasstframe!: string;

    wholemanaflag!: string;
    serialmanaflag!: string;
    islinecontrol!: string;
    sernumunit!: string;

    qualitymanflag!: string;
    qualitynum!: string;
    qualityunit!: string;

    version!: string;
    dr!: string;
    irownum!: string;

    def20!: string;
    def21!: string;
    def50!: string;

    bcolltype_1!: string;
    bcolltype_2!: string;
    bcolltype_3!: string;
    bcolltype_4!: string;
    bcolltype_5!: string;

    updatedAt!: Date;

    static schema: Realm.ObjectSchema = {
        name: 'Material',
        primaryKey: 'code',
        properties: {
            code: 'string',
            pk_group: 'string',
            pk_org: 'string',
            pk_material: 'string',
            pk_marbasclass: 'string',

            name: 'string',
            materialshortname: 'string',
            materialtype: 'string',
            materialbarcode: 'string',
            materialspec: 'string',
            graphid: 'string',

            pk_measdoc: 'string',
            pk_marasstframe: 'string',

            wholemanaflag: 'string',
            serialmanaflag: 'string',
            islinecontrol: 'string',
            sernumunit: 'string',

            qualitymanflag: 'string',
            qualitynum: 'string',
            qualityunit: 'string',

            version: 'string',
            dr: 'string',
            irownum: 'string',

            def20: 'string',
            def21: 'string',
            def50: 'string',

            bcolltype_1: 'string',
            bcolltype_2: 'string',
            bcolltype_3: 'string',
            bcolltype_4: 'string',
            bcolltype_5: 'string',

            updatedAt: 'date'
        }
    };
}

/**
 * 计量单位(MEASDOC)结构化存储
 * - primaryKey: pk_measdoc
 * 目标:
 * - 可通过 pk_measdoc 直接取 name / code / bitnumber 等字段
 * - 也允许通过 code 过滤查询到单位信息
 */
export class MeasDoc extends Realm.Object<MeasDoc> {
    pk_measdoc!: string;
    code!: string;
    name!: string;

    pk_group!: string;
    pk_org!: string;
    dr!: string;
    bitnumber!: string;
    irownum!: string;

    bcolltype_1!: string;
    bcolltype_2!: string;
    bcolltype_3!: string;
    bcolltype_4!: string;
    bcolltype_5!: string;

    updatedAt!: Date;

    static schema: Realm.ObjectSchema = {
        name: 'MeasDoc',
        primaryKey: 'pk_measdoc',
        properties: {
            pk_measdoc: 'string',
            code: 'string',
            name: 'string',

            pk_group: 'string',
            pk_org: 'string',
            dr: 'string',
            bitnumber: 'string',
            irownum: 'string',

            bcolltype_1: 'string',
            bcolltype_2: 'string',
            bcolltype_3: 'string',
            bcolltype_4: 'string',
            bcolltype_5: 'string',

            updatedAt: 'date'
        }
    };
}

2.增量下载逻辑

只拉取本地「最后更新时间」之后的新数据 / 变更数据,节省流量和性能。

//下载下来数据allRows    下载时间 storedAt 
const r = upsertMaterial(realm, allRows, storedAt);
        
/**
 * 写入 MATERIAL(结构化 upsert)
 * - 适合增量下载:只需要把本次返回的 rows upsert 进去即可
 */
export function upsertMaterial(
    realm: Realm,
    rows: any[],
    storedAt: Date
): { count: number; lastTime: string } {
    const updateMode: any = (Realm as any).UpdateMode?.Modified ?? 'modified';
    const safe = (v: any) => String(v ?? '');
    const list = Array.isArray(rows) ? rows : [];

    realm.write(() => {
        for (const r of list) {
            const code = safe(r?.code).trim();
            if (!code) continue;
            realm.create(
                'Material',
                {
                    code,
                    pk_group: safe(r?.pk_group),
                    pk_org: safe(r?.pk_org),
                    pk_material: safe(r?.pk_material),
                    pk_marbasclass: safe(r?.pk_marbasclass),

                    name: safe(r?.name),
                    materialshortname: safe(r?.materialshortname),
                    materialtype: safe(r?.materialtype),
                    materialbarcode: safe(r?.materialbarcode),
                    materialspec: safe(r?.materialspec),
                    graphid: safe(r?.graphid),

                    pk_measdoc: safe(r?.pk_measdoc),
                    pk_marasstframe: safe(r?.pk_marasstframe),

                    wholemanaflag: safe(r?.wholemanaflag),
                    serialmanaflag: safe(r?.serialmanaflag),
                    islinecontrol: safe(r?.islinecontrol),
                    sernumunit: safe(r?.sernumunit),

                    qualitymanflag: safe(r?.qualitymanflag),
                    qualitynum: safe(r?.qualitynum),
                    qualityunit: safe(r?.qualityunit),

                    version: safe(r?.version),
                    dr: safe(r?.dr),
                    irownum: safe(r?.irownum),

                    def20: safe(r?.def20),
                    def21: safe(r?.def21),
                    def50: safe(r?.def50),

                    bcolltype_1: safe(r?.bcolltype_1),
                    bcolltype_2: safe(r?.bcolltype_2),
                    bcolltype_3: safe(r?.bcolltype_3),
                    bcolltype_4: safe(r?.bcolltype_4),
                    bcolltype_5: safe(r?.bcolltype_5),

                    updatedAt: storedAt
                },
                updateMode
            );
        }
        const total = realm.objects<Material>('Material').length;
        upsertMeta(realm, 'MATERIAL', total, storedAt);
    });

    const meta = realm.objectForPrimaryKey<BaseDataMeta>('BaseDataMeta', 'MATERIAL');
    return {
        count: meta?.rowCount ?? realm.objects<Material>('Material').length ?? 0,
        lastTime: toTimeString(meta?.lastStoredAt ?? storedAt)
    };
}

realm.create() 第三个参数是 mode(写入模式)

用来控制当数据主键已存在时,应该怎么处理

3.模式  Realm 提供 3 种模式

1. Realm.UpdateMode.Never(默认)

  • 含义:不更新,只新增
  • 行为:如果主键已存在,直接报错

2. Realm.UpdateMode.Modified(最常用!)

  • 含义存在则更新,不存在则新增(upsert)
  • 行为:根据主键匹配
    • 有这条数据 → 只更新变化的字段
    • 没有这条数据 → 直接新增

    3. Realm.UpdateMode.All

    • 含义:全量覆盖
    • 行为:如果数据存在,直接用新对象覆盖整个对象
    • 所有 写操作(增 / 删 / 改) 必须放在realm.write(() => { ... }) 里面

    • 单条操作靠 主键 定位

    四、常用操作:

    1.查询单条:
    const item = realm.objectForPrimaryKey('Material', code);
    2. 新增单条
    // 新增
    realm.write(() => {
      realm.create('Material', {
        id: Date.now(),       // 唯一主键
        name: '原料仓库',    // 字段
        address: '上海',
        stock: 100,
        updateTime: new Date()
      });
    });
    3. 修改单条(最常用)
    realm.write(() => {
      // 1. 找到要修改的那一条
      const item = realm.objectForPrimaryKey('Material', 123);
    
      if (item) {
        // 2. 直接改字段
        item.name = '修改后的仓库名';
        item.stock = 200;
        item.updateTime = new Date();
      }
    });
    
    realm.write(() => {
      realm.create(
        'Material',
        {
          id: 123,          // 必须传主键
          name: '新名称',
          stock: 200
        },
        Realm.UpdateMode.Modified // 有则更新,无则新增
      );
    });
    4. 删除单条
    realm.write(() => {
      // 1. 找到要删的那一条
      const item = realm.objectForPrimaryKey('Material', 123);
    
      if (item) {
        // 2. 删除
        realm.delete(item);
      }
    });

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    Embrace924

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值