realm-js: 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

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 29
  • Comments: 39 (12 by maintainers)

Commits related to this issue

Most upvoted comments

If people are interested I published to npm an eslint-plugin that bans spread on Realm.Object type. https://www.npmjs.com/package/@waltari/eslint-plugin-ban-realm-spread

@Rob117 You might want to follow #3805 where we are investigating different approaches to enable the spread operator again.

I am closing the issue as the spread operator works with v12.0.0-alpha.1. It will take some time to stabilise the current development before we release v12.0.0 but you are welcome to try our prereleases.

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

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

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

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

This works on 3.7.0-beta.2 but was already broken on first 5.0.0 -release

@ital0 Sorry to hear that. We will update this issue with our progress on the spread operator.

@thaynarbo That does look like a great improvement. Can you create a feature request for this? Then we can get it on our radar.

No update yet I’m afraid. We’ll update the ticket when we do get to look at it.

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.

Captura de Tela 2022-02-09 às 11 57 47

I don’t know what could happen in a huge amount of data.

I have the same issue on Realm 6.1.0. Are there plans to resolve this?

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.

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