rocicorp / replicache

Realtime Sync for Any Backend Stack
https://doc.replicache.dev
993 stars 37 forks source link

React Native support #1013

Open aboodman opened 1 year ago

aboodman commented 1 year ago

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:

izakfilmalter commented 1 year ago

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

aboodman commented 7 months ago

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

darioielardi commented 7 months ago

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.

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.

aboodman commented 7 months ago

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

darioielardi commented 7 months ago

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?

aboodman commented 7 months ago

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.

darioielardi commented 7 months ago

@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!

robsoden commented 6 months ago

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:

['@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.

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!

darioielardi commented 6 months ago

@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:

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

robsoden commented 6 months ago

@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 commented 6 months ago

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,
        });
aboodman commented 6 months ago

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.

darioielardi commented 6 months ago

@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 commented 6 months ago

@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.

robsoden commented 5 months ago

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!

NavaceSystem commented 1 month ago

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!

Ran into the same issues and your fixes worked like a charm. Thank you!

I've pinged the owner of https://github.com/Braden1996/react-native-replicache and he said he'll have a look at the open PRs tomorrow.

Braden1996 commented 1 month ago

Hello all!

Over the weekend I upgraded the React Native Replicache libraries in a few ways. Sorry for not being better with this.

It is worth noting:

@aboodman as for an example app you can add to your test flow, the example provided in that repo should work pretty well and I've added some instructions to the Readme. More than happy to hop on a call and go through it!

Also, regarding EventTarget being introduced, I would suggest removing dom from your internal repo's TSConfig lib. The TS ecosystem has a habit of sticking this in everywhere. We recently removed it from all unnecessary packages at Attio (basically by introduce a "universal types" package which defined the client APIs we expect to be available on all runtimes, like setTimeout).

arv commented 1 month ago

Also, regarding EventTarget being introduced, I would suggest removing dom from your internal repo's TSConfig lib. The TS ecosystem has a habit of sticking this in everywhere. We recently removed it from all unnecessary packages at Attio (basically by introduce a "universal types" package which defined the client APIs we expect to be available on all runtimes, like setTimeout).

I would love to do this. It has been a pet peeve of mine for the last year at least. We do have a lot of bindings (through IDB and fetch) so we need to maintain a copy of those entire type hierarchies. Is your "universal types" package available?

Braden1996 commented 1 month ago

Also, regarding EventTarget being introduced, I would suggest removing dom from your internal repo's TSConfig lib. The TS ecosystem has a habit of sticking this in everywhere. We recently removed it from all unnecessary packages at Attio (basically by introduce a "universal types" package which defined the client APIs we expect to be available on all runtimes, like setTimeout).

I would love to do this. It has been a pet peeve of mine for the last year at least. We do have a lot of bindings (through IDB and fetch) so we need to maintain a copy of those entire type hierarchies. Is your "universal types" package available?

We don't plan on publishing it, unfortunately. Although it's pretty straightforward: mostly a rip of React Native's global types, which you can find here: https://www.npmjs.com/package/react-native?activeTab=code under /react-native/types/modules/globals.d.ts