replicache icon indicating copy to clipboard operation
replicache copied to clipboard

React Native support

Open aboodman opened this issue 2 years ago • 19 comments

Currently it is possible to use Replicache with React Native by implementing experimentalKVStore and hooking it to SQLite. However it's quite manual. We should have first-class support for RN, including:

  • a sample app
  • first-class bindings to SQLite

aboodman avatar Jul 31 '22 02:07 aboodman

This would be much appreciated. If the example used expo that would be a win, https://docs.expo.dev/versions/latest/sdk/sqlite/.

izakfilmalter avatar Nov 25 '22 20:11 izakfilmalter

This is currently being addressed well by https://github.com/Braden1996/react-native-replicache, but we should consider making it first-class.

aboodman avatar Dec 12 '23 03:12 aboodman

This is currently being addressed well by https://github.com/Braden1996/react-native-replicache

@aboodman I've been recently trying to go that way but unfortunately replicache@14 (maybe even from v13) does not work in react native at all.

  • metro cannot resolve replicache dependencies like @rocicorp/logger (can probably be solved with unstable package exports support).
  • apparently replicache now uses EventTarget which is not available in react native

Also, react-native-replicache doesn't seem to work with expo-sqlite@11 because the native module gets imported with the wrong key from the native modules proxy. It doesn't seem maintained at all tbh.

Can you share any plans for an official and reliable solution?

Edit: I just tested with replicache@13, I get ReferenceError: Property 'BroadcastChannel' doesn't exist.

darioielardi avatar Dec 12 '23 09:12 darioielardi

Ah, I did not know that react-native-replicache had stopped working. Thanks for the information.

aboodman avatar Dec 12 '23 19:12 aboodman

Ah, I did not know that react-native-replicache had stopped working. Thanks for the information.

@aboodman It's not just react-native-replicache, actually the problems with that library are easily solvable, the real problems are with the replicache library, which doesn't work in a react native environment anymore because of the use of browser-only APIs like BroadcastChannel and EventTarget.

I'd really like to use Replicache in my mobile apps (I think that's where local-first apps really shine), but these problems related to browser-only APIs make me think that react native support is not even being considered, which is fair but it'd be great to know that before committing to anything.

Would you mind sharing your team's plans about this, if any?

darioielardi avatar Dec 13 '23 14:12 darioielardi

Thanks @darioielardi.

So we fixed the BroadcastChannel dependency in Replicache 14:

https://www.notion.so/replicache/Replicache-14-0-3-3dce462db565425c846875b488677c07?pvs=4#64335524f55f402597dec4c521912e66

This wasn't for React Native, but older Safaris. Unfortunately, we introduced the EventTarget dep at same time:

CleanShot 2023-12-13 at 08 35 18@2x

The real issue here is that we don't have a RN sample, and thus aren't testing RN as part of our release process. I agree you should not rely on RN for anything real if we're not doing that.

I do not have a concrete timeline for this right now other than 2024. I'll have a better idea in January.

aboodman avatar Dec 13 '23 20:12 aboodman

@aboodman that makes sense, I'll look forward to any updates on official React Native support and take some other route in the meantime, thank you!

darioielardi avatar Dec 14 '23 14:12 darioielardi

wanted to post some updates here as we just completed a successful upgrade to replicache 14 on react-native. here are the workarounds to everything we ran into:

  • there's a polyfill for EventTarget (thanks to @aboodman for pointing us toward it) https://www.npmjs.com/package/event-target-polyfill - just add this to your dependencies, and add an import to your project root (in index.js)

  • there were a couple of changes to our babel config that were tricky to figure out. here's our config:

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    '@babel/plugin-transform-flow-strip-types',
    'react-native-reanimated/plugin',
    ['@babel/plugin-transform-private-methods', { loose: true }],
  ],
};

['@babel/plugin-transform-private-methods', { loose: true }] was required by something in the replicache module. however, this broke react-native flatlists in our jest tests. '@babel/plugin-transform-flow-strip-types', was the solution for that.

  • finally, we used patch-package to fix the @rocicorp dependencies for the replicache module. They've been bundled in such a way where the output file is in a out folder, but the package.json is missing a main delcaration, so the bundler gets confused. you can use patch-package with the --exclude flag like so: npx patch-package --exclude '^$' @rocicorp/resolver to add main to each of the sub-packages package.json (resolver, logger, and lock) ex: "main": "./out/logger.js",

also, react-native-replicache is still working for us (thankfully) but we are NOT using expo, so YMMV.

hope all of this this helps somebody!

robsoden avatar Dec 19 '23 15:12 robsoden

@robsoden thank you very much! Inspired by your comment I went ahead and tried to make it work again, this time with @react-native-replicache/react-native-quick-sqlite.

I successfully setup babel and patched all the packages, but I'm still getting errors with @react-native-replicache/react-native-quick-sqlite:

  • with [email protected] I'm getting Quick SQLite Error: Cannot execute query on finalized transaction.
  • with [email protected] and @react-native-replicache/react-native-quick-sqlite patched based on this PR I get Error: unable to open database file.

Do you mind sharing your setup? Or at least what version of these libraries you are using?

darioielardi avatar Dec 20 '23 16:12 darioielardi

@darioielardi glad it was helpful!

so I'm using the following: "@react-native-replicache/react-native-quick-sqlite": "^1.0.0" "react-native-quick-sqlite": "^8.0.4"

and then I've used patch-package to apply those 2 changes to the ReplicacheQuickSQLiteTransaction class (since my PR you found hasn't been merged yet)

the generated patch ends up looking like this:

diff --git a/node_modules/@react-native-replicache/react-native-quick-sqlite/src/replicache-quick-sqlite-transaction.ts b/node_modules/@react-native-replicache/react-native-quick-sqlite/src/replicache-quick-sqlite-transaction.ts
index a86aaa7..710f9a9 100644
--- a/node_modules/@react-native-replicache/react-native-quick-sqlite/src/replicache-quick-sqlite-transaction.ts
+++ b/node_modules/@react-native-replicache/react-native-quick-sqlite/src/replicache-quick-sqlite-transaction.ts
@@ -2,7 +2,7 @@ import { ReplicacheGenericSQLiteTransaction } from "@react-native-replicache/rep
 import * as QuickSQLite from "react-native-quick-sqlite";
 
 export class ReplicacheQuickSQLiteTransaction extends ReplicacheGenericSQLiteTransaction {
-  private _tx: QuickSQLite.TransactionAsync | null = null;
+  private _tx: QuickSQLite.Transaction | null = null;
   private _transactionCommittedSubscriptions = new Set<() => void>();
   private _txCommitted = false;
   private _transactionEndedSubscriptions = new Set<{
@@ -20,7 +20,7 @@ export class ReplicacheQuickSQLiteTransaction extends ReplicacheGenericSQLiteTra
     return await new Promise<void>((resolve, reject) => {
       let didResolve = false;
       try {
-        this.db.transactionAsync(async (tx) => {
+        this.db.transaction(async (tx) => {
           didResolve = true;
           this._tx = tx;
           resolve();

let me know if there's any other info that might help

robsoden avatar Dec 20 '23 17:12 robsoden

oh and also FWIW here's my replicache instantiation, nothing really unique here:

const replicache = new Replicache<MutatorsTypeDef>({
          licenseKey: Config.REPLICACHE_LICENSE_KEY ?? '',
          experimentalCreateKVStore:
            createReplicacheReactNativeQuickSQLiteExperimentalCreateKVStore,
          name: 'your-project',
          mutators,
          pullInterval: null,
          puller: customPuller,
          pusher: customPusher,
          indexes: {
            ...
          },
          pushDelay: 5000,
        });

robsoden avatar Dec 20 '23 17:12 robsoden

If anyone here is interested in contracting for Rocicorp to get this cleaned up, I would pay for it. I want to (a) fix Replicache to install cleanly in rn and expo, (b) if there are any automated unit style tests we can add to avoid regressions do that, (c) have a working sample of todo thst works in in rn and expo with instructions on how to set up and run that any react (web) dev can successfully follow to test and debug changes.

If you’re interested dm me on discord.replicache.dev.

aboodman avatar Dec 20 '23 18:12 aboodman

@robsoden I realized the reason it wasn't working is that I was using user/userId as the client name, which is not a valid filename for the SQLite db 🤦‍♂️

Everything works now! I think I'll try to make it work with expo-sqlite and post any updates here. Thanks again for all your support!

darioielardi avatar Dec 22 '23 12:12 darioielardi

@aboodman apparently on your side the only necessary change is to add the "main" field to every packages.json, 4 out of 5 patches I have are for that (all @rocicorp/ packages and replicache-react).

About the EventTarget polyfill, I guess it can be addressed with a recommendation in the docs.

darioielardi avatar Dec 22 '23 12:12 darioielardi

Anyone else working with Repliache and react-native may have also noticed that react-native-quick-sqlite has recently been deprecated, with the maintainer suggesting a migration to op-sqlite (https://github.com/ospfranco/react-native-quick-sqlite)

quick-sqlite was the main underlying store implemented by the (very excellent) react-native-replicache package (https://github.com/Braden1996/react-native-replicache)

Luckily, thanks to the very solid work on that package, it was pretty trivial to swap in op-sqlite. I've submitted a PR there in the hopes that they'll publish a new adapter.

Feel free to reach out if anyone needs this and has trouble implementing. If there's no movement on the PR over there, we may just publish it ourselves depending on outside interest.

Thanks!

robsoden avatar Feb 14 '24 21:02 robsoden