Closed tharwi closed 10 hours ago
➤ PM Bot commented:
Jira ticket: RJS-2847
@tharwi I am not able to reproduce. How does your code differs from https://github.com/realm/realm-js/pull/6774?
Hi @kneth, thanks for the reply.
Can you change the below line
const updatedProfile = { ...currentProfile, lastAccessDate: new Date() };
to this
const profile = realmInstance.objects('Profile')[0];
const updatedProfile = {...profile, lastAccessDate: new Date()};
@tharwi, the issue you're seeing is more related to what you're assigning rather than the spread operation itself. When you're using the spread operator it performs a shallow copy, so the trackingDataList
on your object will still be the Realm.List
from your currentProfile
.
This means that when you pass that Realm.List
to realm.create(.., .., UpdateMode.Modified)
, it will basically perform a self-assignment (e.g. object.list = object.list
). We do not cache our collections, so even though you're using UpdateMode.Modified
, we currently do not know if it's actually the same list being assigned in this case, and we need to clear the underlying collection being assigned to before adding the items of the RHS list (which is why the list becomes empty here).
This is of course something we'd like to fix as soon as possible and the initial work can be tracked here.
As an example, let's say you want to update the valueToUpdate
property below:
class ListItem extends Realm.Object {
value!: number;
static schema: ObjectSchema = {
name: "ListItem",
properties: {
value: "int",
},
};
}
class ObjectWithList extends Realm.Object {
_id!: BSON.ObjectId;
list!: Realm.List<ListItem>;
valueToUpdate!: string;
static schema: ObjectSchema = {
name: "ObjectWithList",
primaryKey: "_id",
properties: {
_id: "objectId",
list: "ListItem[]",
valueToUpdate: "string",
},
};
}
const realm = new Realm({ schema: [ObjectWithList, ListItem] });
const _id = new BSON.ObjectId();
const object = realm.write(() => {
return realm.create(ObjectWithList, { _id, list: [{ value: 1 }], valueToUpdate: "original" });
});
expect(object.list.length).equals(1);
const objectShallowCopy = { ...object };
// Since it's a shallow copy, the list is still the Realm List.
expect(objectShallowCopy.list).to.be.instanceOf(Realm.List);
realm.write(() => {
// Unfortunately, passing in the same Realm List again basically
// performs a self-assignment, clearing the list.
return realm.create(ObjectWithList, objectShallowCopy, UpdateMode.Modified);
});
// 💥 This will fail.
expect(object.list.length).equals(1);
Workaround:
From the code example you provided, it looks like you only want to update your lastAccessDate
field. You can instead pass only the fields to be updated to realm.create()
when using UpdateMode.Modified
, rather than passing the spread.
realm.write(() => {
- return realm.create(ObjectWithList, objectShallowCopy, UpdateMode.Modified);
+ return realm.create(ObjectWithList, { _id, valueToUpdate: "updated" }, UpdateMode.Modified);
});
Or skip realm.create()
and update the field on the object directly:
realm.write(() => {
- return realm.create(ObjectWithList, objectShallowCopy, UpdateMode.Modified);
+ object.valueToUpdate = "updated";
});
Hi @elle-j , Thanks for the update. Will use one of the workaround for now.
Closing since this is tracked in https://github.com/realm/realm-core/issues/7422.
How frequently does the bug occur?
Always
Description
I am experiencing data loss in nested objects when updating an object in Realm JS using the spread operator. Specifically, after the update, one of my nested lists (trackingDataList) gets reset to an empty list.
Realm models
Code Sample
Before Update:
After Update
Stacktrace & log output
Can you reproduce the bug?
Always
Reproduction Steps
Version
12.10.0
What services are you using?
Local Database only
Are you using encryption?
No
Platform OS and version(s)
iOS 17.2
Build environment
react-native: 0.74.2 node: v18.17.1
Cocoapods version
1.15.2