Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spreading a Realm.Object gives in an empty object #2640

Closed
kraenhansen opened this issue Dec 6, 2019 · 12 comments
Closed

Spreading a Realm.Object gives in an empty object #2640

kraenhansen opened this issue Dec 6, 2019 · 12 comments
Labels

Comments

@kraenhansen
Copy link
Member

kraenhansen commented Dec 6, 2019

Goals

I would like to be able to easily convert a Realm.Object to a plain-ol'-javascript-object.

Expected Results

When doing a JS spread on a Realm.Object I would expect the resulting object contained the same properties as the Realm.Object.

Actual Results

You get an empty object.

Steps to Reproduce & Code Sample

const Realm = require('realm')
const realm = new Realm({ schema: [ { name: "Person", properties: { name: "string" } } ] });
realm.write(() => { realm.create("Person", { name: "Alice" }) });
const persons = realm.objects("Person"); // Results { [0]: RealmObject { [name]: 'Alice' } }
const alice = persons[0]; // RealmObject { [name]: 'Alice' }
Object.keys(alice) // [ 'name' ]
Object.getOwnPropertyNames(alice) // [ '_realm', 'name' ]
alice.hasOwnProperty('name') // true
Object.getOwnPropertyDescriptors(alice)
/* 
{ _realm:
   { value: Realm {},
     writable: true,
     enumerable: false,
     configurable: false },
  name:
   { value: 'Alice',
     writable: true,
     enumerable: false,
     configurable: true } }
*/
{ ...alice } // {}

An interesting observation is that getOwnPropertyNames contains 'name' but Object.hasOwnProperty(alice, 'name') is false ‍♂️

Version of Realm and Tooling

  • Realm JS SDK Version: 3.5.0
  • Node or React Native: Node v10.17.0
  • Client OS & Version: N/A
  • Which debugger for React Native: None
@radeno
Copy link

radeno commented Dec 10, 2019

Spread operator is very tricky and makes confusing about behaviour. It doesn't copy getters/setters, just values:
https://2ality.com/2016/10/rest-spread-properties.html#both-spread-and-object.assign()-read-values-via-a-“get”-operation
and implentation in RealmObject class

schema.properties.forEach((name) => {

Anyway. hasOwnProperty is not class method, it is object method. You need to call this like this alice.hasOwnProperty('name') or Object.prototype.hasOwnProperty.call(alice, 'name')

@kraenhansen
Copy link
Member Author

@radeno thanks for your interest.

To me the spread operator is pretty straight forward - it makes sense to me that it doesn't copy getters/setters, but this is also not what this issue is about. Thanks for the hint on the use of hasOwnProperty - but honestly, that was just there to show what the value might be: I've updated the code.

I believe the root cause of this issue is that both spread and Object.assign() only consider own enumerable properties:

Object.getOwnPropertyDescriptors(alice)
/* 👇
{ _realm:
   { value: Realm {},
     writable: true,
     enumerable: false,
     configurable: false },
  name:
   { value: 'Alice',
     writable: true,
     enumerable: false,
     configurable: true } }
*/

@davidegironi
Copy link

Same issue here, tested on RN
"realm": "ˆ5.0.3"
"react": "^16.13.1"
"react-native": "0.62.2"

I'trying to load an Object to a const, that I'm using with the useState hook, that's because i can change the full object fields with just one useState setter callback
const testobj = realm.objectForPrimaryKey(...
Then if i spread testobj the return value is empty {}
Also I've try to take the first element from objects, same result, spread is empty
const testobj = realm.objects(... )[0]

I've try the JSON.parse(JSON.stringify(testobj)) trick, but I've no luck with dates that became strings, and also it's not that beauty.
Any other suggestion to stread a Realm.Object or a Realm.Result?

thank you!

@davidegironi
Copy link

davidegironi commented Apr 24, 2020

For those who are interested in, I've found a quick fix here, I'm testing it (mapProps method) #141 (comment)

@Akash-T2S
Copy link

Even i'm facing the same issue where hasOwnProperty throw false.

@Bandwagoner
Copy link

Bandwagoner commented Oct 23, 2020

Created an alternative solution in typescript. Haven't tried using shallow clone, but deep clone seems to do the job. Obviously you can implement your own cloneDeep function, but lodash is used here out of pure laziness.

import cloneDeep from 'lodash-es/cloneDeep';

export function deepCloneRealmObject<T>(realmObject: Realm.Object): T {
    const clone: any = {};
    realmObject.keys().forEach((key) => {
        if (typeof realmObject[key] == 'object') {
            clone[key] = cloneDeep(realmObject[key]);
        } else {
            clone[key] = realmObject[key];
        }
    });

    return clone;
}

@hossein-zare
Copy link

This works for me.

let data = realm.objectForPrimaryKey('User', 10);

data = JSON.parse(JSON.stringify(data));

console.log({...data, name: 'Bob'});

@BruceSuperProgramer
Copy link

We found lodash functions such as orderBy and isEmpty not working with realmObject anymore which worked well with the legacy realm version.

@mfbx9da4
Copy link

mfbx9da4 commented Feb 9, 2022

import Realm from 'realm'

export function shallowCloneRealmObject<T extends Realm.Object>(x: T) {
  const copy: any = {}
  for (const key in x) {
    copy[key] = x[key]
  }
  return copy as Exclude<T, Realm.Object>
}

My workaround for now:

  • It's a shallow clone so very close to spread
  • The cloned object won't be a live realm object which is what you would expect when spreading. This is also reflected by the types.

@Waltari10
Copy link

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

(hopefully spread operator wont be fixed now the next day)

@greenafrican
Copy link

Note that JSON.parse(JSON.stringify(data)) will convert all ObjectIds to strings.

@sync-by-unito sync-by-unito bot closed this as completed Sep 11, 2023
@kneth
Copy link
Contributor

kneth commented Sep 11, 2023

Version 12.0.0 introduced support for the spread operator

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests