Skip to content

Commit

Permalink
Add a workaround for React Native Proxy objects pre-JSI not working…
Browse files Browse the repository at this point in the history
… properly with JSC.

This change can be reverted once we go to v11. Fixes #4507
  • Loading branch information
Tom Duncalf committed Apr 29, 2022
1 parent 9c589ad commit 4c0cc9c
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 5 deletions.
10 changes: 9 additions & 1 deletion lib/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,6 @@ module.exports = function (realmConstructor) {
Object.defineProperties(realmConstructor.User.prototype, getOwnPropertyDescriptors(userMethods.instance));

let subscriptionSetMethods = require("./subscription-set");

Object.defineProperties(
realmConstructor.App.Sync.SubscriptionSet,
getOwnPropertyDescriptors(subscriptionSetMethods.static),
Expand All @@ -376,6 +375,15 @@ module.exports = function (realmConstructor) {
...require("./collection-methods")(realmConstructor),
});

let mutableSubscriptionSetMethods = require("./mutable-subscription-set");
Object.defineProperties(
realmConstructor.App.Sync.MutableSubscriptionSet,
getOwnPropertyDescriptors(mutableSubscriptionSetMethods.static),
);
Object.defineProperties(realmConstructor.App.Sync.MutableSubscriptionSet.prototype, {
...getOwnPropertyDescriptors(mutableSubscriptionSetMethods.instance(realmConstructor)),
});

let sessionMethods = require("./session");
Object.defineProperties(realmConstructor.App.Sync.Session, getOwnPropertyDescriptors(sessionMethods.static));
Object.defineProperties(
Expand Down
50 changes: 50 additions & 0 deletions lib/mutable-subscription-set.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2021 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

import { symbols } from "@realm.io/common";

// React Native `Proxy` objects when not using JSI are not compatible with
// `JSValueIsObjectOfClass`, because it seems JSC is not able to "unwrap"
// the proxy - see https://github.com/realm/realm-js/issues/4507#issuecomment-1112237740.
//
// In order to enable the return value of Realm React's `useQuery` (which are wrapped
// in a proxy) to be passed to the subscription mutation methods, we need to store
// the unproxied results on the proxy object as a non-enumerable field with a symbol key
// (see `useQuery.ts` in @realm/react), then in here, we check if that field exists, and
// if so we pass the original unproxied results to C++.
//
// Once our v11 branch is merged, we can revert this change as JSI React Native `Proxy`s
// work fine, by reverting PR #xxxx.
const instanceMethods = (realmConstructor) => ({
add(query, options) {
return this._add(query[symbols.REALM_REACT_PROXIED_OBJECT] || query, options);
},

remove(query) {
return this._remove(query[symbols.REALM_REACT_PROXIED_OBJECT]);
}
});

const staticMethods = {
// none
};

module.exports = {
static: staticMethods,
instance: instanceMethods,
};
1 change: 1 addition & 0 deletions packages/realm-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
////////////////////////////////////////////////////////////////////////////

export { deprecationWarning, handleDeprecatedPositionalArgs } from "./deprecation";
export * as symbols from "./symbols";
1 change: 1 addition & 0 deletions packages/realm-common/src/symbols.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const REALM_REACT_PROXIED_OBJECT = Symbol('REALM_REACT_PROXIED_OBJECT');
3 changes: 2 additions & 1 deletion packages/realm-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
},
"dependencies": {
"lodash": "^4.17.21",
"realm": ">=10"
"realm": ">=10",
"@realm.io/common": "*"
},
"devDependencies": {
"@babel/core": "^7.15.5",
Expand Down
16 changes: 15 additions & 1 deletion packages/realm-react/src/useQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import Realm from "realm";
import { useEffect, useReducer, useMemo } from "react";
import { createCachedCollection } from "./cachedCollection";
import { symbols } from "@realm.io/common";

/**
* Generates the `useQuery` hook from a given `useRealm` hook.
Expand Down Expand Up @@ -67,6 +68,19 @@ export function createUseQuery(useRealm: () => Realm) {

// This makes sure the collection has a different reference on a rerender
// Also we are ensuring the type returned is Realm.Results, as this is known in this context
return new Proxy(collection as Realm.Results<T & Realm.Object>, {});
const proxy = new Proxy(collection as Realm.Results<T & Realm.Object>, {});

// Store the original, unproxied result as a non-enumerable field with a symbol
// key on the proxy object, so that we can check for this and get the original results
// when passing the result of `useQuery` into the subscription mutation methods
// (see `lib/mutable-subscription-set.js` for more details)
Object.defineProperty(proxy, symbols.REALM_REACT_PROXIED_OBJECT, {
value: realm.objects(type),
enumerable: false,
configurable: false,
writable: true,
});

return proxy;
};
}
4 changes: 2 additions & 2 deletions src/js_subscriptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -565,9 +565,9 @@ class MutableSubscriptionSetClass : public ClassDefinition<T, MutableSubscriptio
{"findByQuery", wrap<SubscriptionSetClass<T>::find_by_query>},

// Mutable-only methods
{"add", wrap<add>},
{"_add", wrap<add>},
{"removeByName", wrap<remove_by_name>},
{"remove", wrap<remove>},
{"_remove", wrap<remove>},
{"removeSubscription", wrap<remove_subscription>},
{"removeAll", wrap<remove_all>},
{"removeByObjectType", wrap<remove_by_object_type>},
Expand Down

0 comments on commit 4c0cc9c

Please sign in to comment.