Skip to content

Commit 142f06f

Browse files
authored
(DOCSP-26993): @realm/react-ify 'Embedded Objects - React Native SDK' (#2486)
## Pull Request Info ### Jira - https://jira.mongodb.org/browse/DOCSP-26993 ### Staged Changes - [Embedded Objects](https://docs-mongodbcom-staging.corp.mongodb.com/realm/docsworker-xlarge/DOCSP-26993/sdk/react-native/realm-database/schemas/embedded-objects/) ### Reminder Checklist If your PR modifies the docs, you might need to also update some corresponding pages. Check if completed or N/A. - [x] Create Jira ticket for corresponding docs-app-services update(s), if any - [x] Checked/updated Admin API - [x] Checked/updated CLI reference ### Review Guidelines [REVIEWING.md](https://github.com/mongodb/docs-realm/blob/master/REVIEWING.md)
1 parent eb10407 commit 142f06f

26 files changed

+1320
-27
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Realm from 'realm';
2+
3+
// :snippet-start: js-address-schema
4+
class Address extends Realm.Object {
5+
static schema = {
6+
name: "Address",
7+
embedded: true, // default: false
8+
properties: {
9+
street: "string?",
10+
city: "string?",
11+
country: "string?",
12+
postalCode: "string?",
13+
},
14+
};
15+
}
16+
// :snippet-end:
17+
export default Address;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Realm from 'realm';
2+
3+
// :snippet-start: js-business-schema
4+
class Business extends Realm.Object {
5+
static schema = {
6+
name: "Business",
7+
primaryKey: "_id",
8+
properties: {
9+
_id: "objectId",
10+
name: "string",
11+
addresses: { type: "list", objectType: "Address" }, // Embed an array of objects
12+
},
13+
};
14+
}
15+
// :snippet-end:
16+
export default Business;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Realm from 'realm';
2+
3+
// :snippet-start: js-contact-schema
4+
class Contact extends Realm.Object {
5+
static schema = {
6+
name: "Contact",
7+
primaryKey: "_id",
8+
properties: {
9+
_id: "objectId",
10+
name: "string",
11+
address: "Address", // Embed a single object
12+
},
13+
};
14+
}
15+
// :snippet-end:
16+
export default Contact;
Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
import React, {useState} from 'react';
2+
import {Button, TextInput, View, Text} from 'react-native';
3+
import {render, fireEvent, waitFor, act} from '@testing-library/react-native';
4+
import Realm from 'realm';
5+
import {createRealmContext} from '@realm/react';
6+
import Address from '../../Models/Address';
7+
import Contact from '../../Models/Contact';
8+
9+
const realmConfig = {
10+
schema: [Address, Contact],
11+
deleteRealmIfMigrationNeeded: true,
12+
};
13+
14+
const {RealmProvider, useQuery, useRealm} = createRealmContext(realmConfig);
15+
16+
let assertionRealm;
17+
18+
// test describe block for the embedded objects page
19+
describe('embedded objects tests', () => {
20+
beforeEach(async () => {
21+
// we will use this Realm for assertions to access Realm Objects outside of a Functional Component (like required by @realm/react)
22+
assertionRealm = await Realm.open(realmConfig);
23+
24+
// // delete every object in the realmConfig in the Realm to make test idempotent
25+
assertionRealm.write(() => {
26+
assertionRealm.delete(assertionRealm.objects(Contact));
27+
28+
new Contact(assertionRealm, {
29+
name: 'John Smith',
30+
_id: new Realm.BSON.ObjectID(),
31+
address: {
32+
street: '1 Home Street',
33+
city: 'New York City',
34+
country: 'USA',
35+
postalCode: '12345',
36+
},
37+
});
38+
39+
new Contact(assertionRealm, {
40+
name: 'Jane Doe',
41+
_id: new Realm.BSON.ObjectID(),
42+
address: {
43+
street: '2 Home Street',
44+
city: 'Kansas City',
45+
country: 'USA',
46+
postalCode: '54321',
47+
},
48+
});
49+
});
50+
});
51+
afterAll(() => {
52+
// close realm
53+
if (!assertionRealm.isClosed) {
54+
assertionRealm.close();
55+
}
56+
});
57+
it("should create and read an embedded object", async () => {
58+
// :snippet-start: create-embedded-object
59+
// :replace-start: {
60+
// "terms": {
61+
// "LeBron James": "",
62+
// "1 Goat Drive": "",
63+
// "Cleveland": "",
64+
// "USA": "",
65+
// "12345": "",
66+
// " testID='submitContactBtn'": ""
67+
// }
68+
// }
69+
70+
const CreateContact = () => {
71+
const [name, setContactName] = useState('LeBron James');
72+
const [street, setStreet] = useState('1 Goat Drive');
73+
const [city, setCity] = useState('Cleveland');
74+
const [country, setCountry] = useState('USA');
75+
const [postalCode, setPostalCode] = useState('12345');
76+
const realm = useRealm();
77+
78+
const submitContact = () => {
79+
// Create a Contact within a write transaction
80+
realm.write(() => {
81+
// Create an embedded Address object
82+
const address = {
83+
street,
84+
city,
85+
country,
86+
postalCode,
87+
};
88+
new Contact(realm, {
89+
_id: new Realm.BSON.ObjectID(),
90+
name,
91+
address, // Embed the address in the Contact object
92+
});
93+
});
94+
};
95+
return (
96+
<View>
97+
<TextInput value={name} onChangeText={text => setContactName(text)} />
98+
<TextInput value={street} onChangeText={text => setStreet(text)} />
99+
<TextInput value={city} onChangeText={text => setCity(text)} />
100+
<TextInput value={country} onChangeText={text => setCountry(text)} />
101+
<TextInput value={postalCode} onChangeText={text => setPostalCode(text)} />
102+
<Button title='Submit Contact' testID='submitContactBtn' onPress={submitContact} />
103+
</View>
104+
);
105+
};
106+
// :replace-end:
107+
// :snippet-end:
108+
const App = () => (
109+
<RealmProvider>
110+
<CreateContact/>
111+
</RealmProvider>
112+
);
113+
const {findByTestId} = render(<App />);
114+
const submitContactBtn = await waitFor(() => findByTestId('submitContactBtn'), {
115+
timeout: 5000,
116+
});
117+
await act(async () => {
118+
fireEvent.press(submitContactBtn);
119+
});
120+
// check if the new Contact object has been created
121+
const contact = assertionRealm.objects(Contact).filtered("name == 'LeBron James'")[0];
122+
expect(contact.name).toBe('LeBron James');
123+
expect(contact.address.street).toBe('1 Goat Drive');
124+
expect(contact.address.city).toBe('Cleveland');
125+
expect(contact.address.country).toBe('USA');
126+
expect(contact.address.postalCode).toBe('12345');
127+
});
128+
it('should query for an embedded object', async () => {
129+
// :snippet-start: query-embedded-object
130+
// :replace-start: {
131+
// "terms": {
132+
// " testID = 'addressText'": ""
133+
// }
134+
// }
135+
const ContactList = () => {
136+
// Query for all Contact objects
137+
const contacts = useQuery(Contact);
138+
139+
// Run the `.filtered()` method on all the returned Contacts to find the
140+
// contact with the name "John Smith" and the corresponding street address
141+
const contactAddress = contacts
142+
.filtered("name == 'John Smith'")[0].address.street;
143+
144+
return(
145+
<View>
146+
<Text>John Smith's street address:</Text>
147+
<Text testID = 'addressText'>{contactAddress}</Text>
148+
</View>
149+
);
150+
};
151+
// :replace-end:
152+
// :snippet-end:
153+
const App = () => (
154+
<RealmProvider>
155+
<ContactList />
156+
</RealmProvider>
157+
);
158+
const {getByTestId} = render(<App />);
159+
160+
// test that querying for name works
161+
const contactAddress = await waitFor(() => getByTestId('addressText'), {
162+
timeout: 5000,
163+
});
164+
expect(contactAddress.props.children).toBe('1 Home Street');
165+
});
166+
it('should delete an embedded object', async () => {
167+
// :snippet-start: delete-embedded-object
168+
// :replace-start: {
169+
// "terms": {
170+
// " testID = 'contactNameText'": "",
171+
// " testID = 'deleteContactBtn'": ""
172+
// }
173+
// }
174+
const ContactInfo = ({contactName}) => {
175+
const contacts = useQuery(Contact);
176+
const toDelete = contacts.filtered(`name == '${contactName}'`)[0]
177+
const realm = useRealm();
178+
179+
const deleteContact = () => {
180+
realm.write(() => {
181+
// Deleting the contact also deletes the embedded address of that contact
182+
realm.delete(
183+
toDelete
184+
);
185+
});
186+
};
187+
return (
188+
<View>
189+
<Text testID='contactNameText'>{contactName}</Text>
190+
<Button testID='deleteContactBtn' onPress={deleteContact} title='Delete Contact' />
191+
</View>
192+
);
193+
};
194+
// :replace-end:
195+
// :snippet-end:
196+
const App = () => (
197+
<RealmProvider>
198+
<ContactInfo contactName='John Smith'/>
199+
</RealmProvider>
200+
);
201+
const {findByTestId} = render(<App />);
202+
const contactNameText = await waitFor(() => findByTestId('contactNameText'), {
203+
timeout: 5000,
204+
});
205+
expect(contactNameText.props.children).toBe('John Smith');
206+
207+
const deleteContactBtn = await waitFor(() => findByTestId('deleteContactBtn'), {
208+
timeout: 5000,
209+
});
210+
await act(async () => {
211+
fireEvent.press(deleteContactBtn);
212+
});
213+
// check if the new Contact object has been deleted
214+
const contact = assertionRealm.objects(Contact)
215+
expect(contact.length).toBe(1);
216+
});
217+
it("should update an embedded object", async () => {
218+
// :snippet-start: update-embedded-object
219+
// :replace-start: {
220+
// "terms": {
221+
// " testID='updateContactBtn'": "",
222+
// "3 jefferson lane": ""
223+
// }
224+
// }
225+
// Find the contact you want to update
226+
const UpdateContact = ({contactName}) => {
227+
const [street, setStreet] = useState('3 jefferson lane');
228+
const contact = useQuery(Contact).filtered(`name == '${contactName}'`)[0];
229+
const realm = useRealm();
230+
231+
const updateStreet = () => {
232+
// Modify the property of the embedded Address object in a write transaction
233+
realm.write(() => {
234+
// Update the address directly through the contact
235+
contact.address.street = street;
236+
});
237+
};
238+
return (
239+
<View>
240+
<Text>{contact.name}</Text>
241+
<TextInput value={street} onChangeText={setStreet} placeholder='Enter New Street Address' />
242+
<Button testID='updateContactBtn' onPress={updateStreet} title='Update Street Address' />
243+
</View>
244+
);
245+
};
246+
// :replace-end:
247+
// :snippet-end:
248+
const App = () => (
249+
<RealmProvider>
250+
<UpdateContact contactName='John Smith'/>
251+
</RealmProvider>
252+
);
253+
const {findByTestId} = render(<App />);
254+
const updateContactBtn = await waitFor(() => findByTestId('updateContactBtn'), {
255+
timeout: 5000,
256+
});
257+
await act(async () => {
258+
fireEvent.press(updateContactBtn);
259+
});
260+
// check if the new Contact object has been updated
261+
const contact = assertionRealm.objects(Contact).filtered("name == 'John Smith'")[0];
262+
expect(contact.address.street).toBe('3 jefferson lane');
263+
});
264+
it("should overwrite an embedded object", async () => {
265+
// :snippet-start: overwrite-embedded-object
266+
// :replace-start: {
267+
// "terms": {
268+
// " testID='overwriteContactBtn'": "",
269+
// "12 Grimmauld Place": "",
270+
// "London": "",
271+
// "UK": "",
272+
// "E1 7AA": ""
273+
// }
274+
// }
275+
const OverwriteContact = ({contactName}) => {
276+
const [street, setStreet] = useState('12 Grimmauld Place');
277+
const [city, setCity] = useState('London');
278+
const [country, setCountry] = useState('UK');
279+
const [postalCode, setPostalCode] = useState('E1 7AA');
280+
const contact = useQuery(Contact).filtered(`name == '${contactName}'`)[0];
281+
const realm = useRealm();
282+
283+
const updateAddress = () => {
284+
realm.write(() => {
285+
// Within a write transaction, overwrite the embedded object with the new address
286+
const address = {
287+
street,
288+
city,
289+
country,
290+
postalCode,
291+
};
292+
contact.address = address;
293+
});
294+
};
295+
return (
296+
<View>
297+
<Text>{contact.name}</Text>
298+
<Text>Enter the new address:</Text>
299+
<TextInput value={street} onChangeText={setStreet} placeholder='Street' />
300+
<TextInput value={city} onChangeText={setCity} placeholder='City' />
301+
<TextInput value={country} onChangeText={setCountry} placeholder='Country' />
302+
<TextInput value={postalCode} onChangeText={setPostalCode} placeholder='Postal Code' />
303+
<Button testID='overwriteContactBtn' onPress={updateAddress} title='Overwrite Address' />
304+
</View>
305+
);
306+
};
307+
// :replace-end:
308+
// :snippet-end:
309+
const App = () => (
310+
<RealmProvider>
311+
<OverwriteContact contactName='John Smith'/>
312+
</RealmProvider>
313+
);
314+
const {findByTestId} = render(<App />);
315+
const overwriteContactBtn = await waitFor(() => findByTestId('overwriteContactBtn'), {
316+
timeout: 5000,
317+
});
318+
await act(async () => {
319+
fireEvent.press(overwriteContactBtn);
320+
});
321+
// check if the new Contact object has been overwritten
322+
const contact = assertionRealm.objects(Contact).filtered("name == 'John Smith'")[0];
323+
expect(contact.address.street).toBe('12 Grimmauld Place');
324+
expect(contact.address.city).toBe('London');
325+
expect(contact.address.country).toBe('UK');
326+
expect(contact.address.postalCode).toBe('E1 7AA');
327+
});
328+
});

0 commit comments

Comments
 (0)