realm-js
realm-js copied to clipboard
Spread operation doesn't work
Goals
Spread operation works on older versions , After upgrading from 3.4.1 to 5.0.3 , Spread operation not works
Expected Results
Spread operation works on older versions , I passed ream object as props to react component by spreading it
<MyComponent {...realmObject}>
Actual Results
Speading creates empty object
Steps to Reproduce
const CarSchema = {
name: 'Car',
properties: {
make: 'string',
model: 'string',
miles: {type: 'int', default: 0},
}
};
Realm.open({schema: [CarSchema]})
.then(realm => {
// Create Realm objects and write to local storage
realm.write(() => {
const myCar = realm.create('Car', {
make: 'Honda',
model: 'Civic',
miles: 1000,
});
myCar.miles += 20; // Update a property value
});
// Query Realm for all cars with a high mileage
const car = realm.objects('Car')[0];
const spread = {...car};
console.log(spread)
// Remember to close the realm when finished.
realm.close();
})
.catch(error => {
console.log(error);
});
Code Sample
Version of Realm and Tooling
- Realm JS SDK Version: 5.0.3
- Node or React Native: 10.15.3 - 0.62.2
- Client OS & Version: ? Windows 10 10.0.18362
- Which debugger for React Native: None
+1
This bug is stopping us from being able to use v5 of Realm as spreading is extremely common. We can't guarantee someone down the line won't try to spread one of the results we got, and when they do it will inexplicably be an empty object.
The only workaround I see is to manually copy each property out of the result into a new object. However that requires reading every single result into memory, breaking the lazy loading of Realm that is so valuable. It also adds a ton of extra computations and specific code for each object type to grab the properties.
This works on 3.7.0-beta.2 but was already broken on first 5.0.0 -release
+1
Thanks for reporting this.
My investigation shows that this was not working in nodejs in realm 3. I assume you have it working in React Native with Realm 3. This was either a bug in our nodejs implementation for realm 3 or it was purposely done since all Realm Object properties in nodejs were defined as not enumerable.
In Realm 5 all properties of the Realm.Objects are defined as accessor properties on the prototype object and are not own properties of the instance . That's why spread operator is not working for these objects. This is a breaking change and is one of the requirements to be able to implement Realm with N-API
If you need to clone realm objects I would suggest doing something like this
const Realm = require('realm');
Object.defineProperty(Realm.Object.prototype, "clone", {
value: function () {
let result = {};
for (const key in this) {
result[key] = this[key];
}
return result;
},
writable: true,
configurable: true,
enumerable: false
});
and using it like so
<MyComponent {...realmObject.clone()}>
Since we already provide realmObject.keys()
and realmObject.entries()
for every Realm Object in Realm 5. We could provide this method as well so its available by default.
cheers
@josephmbeveridge Could you elaborate more on the laziness part.
In particular this
However that requires reading every single result into memory, breaking the lazy loading of Realm that is so valuable.
I don't think you can use spread operator to retain the lazy properties of realm onto the cloned object. Or I am getting this wrong.
I would like to understand more why you can't migrate to Realm 5.
cheers
@blagoev Since we don't want objects that can't be spread as they are dangerous in the rest of our app, we need to convert every single result from realm into a proper js object that follows the normally expected spreading rules. This requires reading each item from the result one at a time, forcing it all into memory, and converting it to an object with properties instead of accessors on the prototype.
If the results worked with spread already, we wouldn't have to perform any operations on them and could just pass them on. This lets us keep the lazy loading, and avoid reading objects until we actually need them.
To add on to this more, the rest of our app shouldn't have to be aware of results coming from Realm and having to perform different operations to get the data out of the object than we would for remote results. This breaks our data abstraction layer and makes development much more complicated and error-prone.
Thanks for taking the time to look into this btw :)
@josephmbeveridge Thanks for the clarification.
Realm Objects are also proper js object that follows the normally expected spreading rules
. The difference is they don't define own value properties but accessor properties in Realm 5.
What you describe is reasonable. If you control the spreading
code as well you could make it work. The idea of passing realm objects around is still working, but you need to change the code responsible for spreading the object to {...realmObject.clone()}
to achieve the desired behavior.
Previously Realm Objects were behaving as magic objects. both like JS proxies and defining own value properties. This is not possible with N-API in its current form for Realm.
Another option might be to proxify the objects just before passing them to the code responsible for spreading
. You could do that also for collections and retrieve objects only when needed. This might as well not be feasible for your case.
let realmObject = realm.create(.....
const handler = {
ownKeys(target) {
return target.keys()
}
};
const obj = new Proxy(realmObject, handler);
var spread = {...obj}
cheers
@blagoev Thanks for the response.
Unfortunately having code above the database layer that knows we are using realm and does specific operations because we are using realm isn't going to work for us. We are intentionally abstracting the data layer to provide better separation of logic and hide implementation details. This not only protects us from changes in the database layer, but lets us use more than one data source for the same UI and business logic code, without them knowing where the data comes from. This is a fairly common pattern in large scale applications and one we use often, especially when we have to pull data from multiple sources (local db, remote server, etc) depending on the situation. This pattern of abstracting where the data comes from means that we cannot run an operation to extract data for only objects that came from realm (unless we do it very early and for all of the results, defeating the purpose of lazy loading as I was mentioning before) because we don't actually know where a given object came from (intentionally).
Of course our implementation isn't your problem, but I think this will be a fairly common issue for larger code bases that want to properly insulate their data layer.
The problem is also with external libraries like monocle.ts that previously managed to copy all the properties from realm objects but fail with Realm 5.x
Cloning before modifying would work but finding all the places where this happens is tedious work in a large RN application.
Maybe a lazy proxy over a Realm Collection that would do the clone once objects are accessed would be the best solution here.
Is the problem still there?
@forkeer Yes
The same for me(
Same issue here, code that worked on Realm v2.10.0 is now broken on Realm v6.0.2. Besides passing all properties at once, another use case is to use a local override while moving objects on the screen for example:
<Component position={{...realmData.position, ..this.state.positionOverride}} />
As a workaround, Object.assign
can be used for this
<Component position={Object.assign(realmData.position, this.state.positionOverride)} />
Seems like the bug is located in the hasOwnProperty
method for Realm objects:
for (let a in realmObject)
console.log(a, realmObject.hasOwnProperty(a));
Will output:
prop1 false
prop2 false
....
In case it helps, the project I'm having this issue with is a React-Native app (RN v0.62.2) for Android (did not test on iOS yet)
Same thing on realm 6
@kneth @blagoev @fealebenpae take a look please!!
I have the same issue on Realm 6.1.0. Are there plans to resolve this?
@blagoev The .clone()
method works. Instead of spreading the realm object I spread the cloned realm object and it works like the older verion I used (4.0.0-beta.2).
Are there any significant drawbacks in using clone?
Typescript is misleading if you use clone. There is nothing to suggest anywhere that the Realm Object returned would not support the spread operator to clone like it would in a normal object. This means that, as others have mentioned, unless someone on the project tells you specifically that Realm does not support spread operators anymore, you have no way of knowing that it won't copy properties defined in the schema to a new object.
This is understandable when the implementation is explained, but it is unintuitive and strange behavior for an object to have. If I have an array of objects retrieved from the database that I have fed into a form, I need to
- Retrieve the objects from Realm. No problem.
2a) Use the specific clone operator on each object before returning it from my database management layer (meaning if the method retrieving the obejcts doesn't edit them, this is a ton of wasted operations because I am cloning each object in a query) or
2b) need to have all the code written outside of the DB layer call
clone()
at the appropriate time, which breaks encapusulation of DB logic and requires that you ignore typescript support because nothing in TS will tell you that spread doesn't work on a DB realm object even though each property of that object is normally accessible.
For example, it is really unintuitive that intellisense and Typescript both say that
realmResultObj.propertyA // works as expected
{ ...realmResultObj }.propertyA // undefined, this is not intuitive at all
Not being able to support the spread operator is what it is, but is it at least possible to change typescript return values so it doesn't return types of <mySchema & Realm.Object> because it doesn't actually fulfill all of the requirements of an object instantiated of type <mySchema>
@Rob117 You might want to follow #3805 where we are investigating different approaches to enable the spread operator again.
What is the progress of adding spread operator support for realm objects? Is there any workaround for now?
What is the progress of adding spread operator support for realm objects? Is there any workaround for now?
@hellforever well I'm using realm 10.12.0 and when I use models.objects(modelName).toJSON()
is solving my problem by now.
This method is parsing the Realm Object to JS Object and I'm able to use spread operator.
Also I couldn't found any reference at RealmJS API Docs.

I don't know what could happen in a huge amount of data.
Hi @ital0, I believe this is an oversight in our documentation, sorry about that. I will create a ticket for us to update the docs to add toJSON
@ital0 Thanks for your answer. But toJSON() may have an impact on performance. In case we need to do some calculation, override some properties of a realm object, or add new properties to a realm object. Do you know any better ways to do it? If someone can give some advice, I will appreciate it so much
@hellforever In my case I have all the Realm access abstracted in a separated layer. After getting the data I do all the rest (override or add some properties, do some calculations).
In my tests doing toJSON
in 100k data I didn't feel any performance issues.
Just to add that we are planning to add spread operator support, but we need to ensure we do so in a way which doesn't significantly impact normal performance, so this is an going work in active progress.
It's good to hear that the toJSON
workaround doesn't seem to impact real world performance too much for now
Back here just to give feedback about toJSON
method and performance.
After doing more testing I can confirm that when you are managing a large amount of data the performance drops considerably.
Probably I'll have to wait spread operator support :(
@ital0 Sorry to hear that. We will update this issue with our progress on the spread operator.
@tomduncalf any news about allowing spread operators on Realm Objects? I'm already following #3805 but I didn't receive any news anymore.
No update yet I'm afraid. We'll update the ticket when we do get to look at it.