react-native-template-realm-js
Advanced tools
Comparing version 0.0.3 to 0.0.4
{ | ||
"name": "react-native-template-realm-js", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"description": "React Native Template Realm JS", | ||
"scripts": { | ||
"lint": "cd template && npm run lint" | ||
}, | ||
"repository": { | ||
@@ -16,2 +19,2 @@ "type": "git", | ||
"license": "Apache-2" | ||
} | ||
} |
@@ -1,3 +0,3 @@ | ||
import React, { useState, useEffect, useRef, useCallback } from 'react'; | ||
import { SafeAreaView, View, StyleSheet } from 'react-native'; | ||
import React, {useState, useEffect, useRef, useCallback} from 'react'; | ||
import {SafeAreaView, View, StyleSheet} from 'react-native'; | ||
import Realm from 'realm'; | ||
@@ -21,9 +21,2 @@ | ||
useEffect(() => { | ||
openRealm(); | ||
// Return a cleanup callback to close the realm to prevent memory leaks | ||
return closeRealm; | ||
}, []); | ||
const openRealm = useCallback(async () => { | ||
@@ -43,9 +36,10 @@ try { | ||
realmRef.current = realm; | ||
// When querying a realm to find objects (e.g. realm.objects('Tasks')) the result we get back | ||
// and the objects in it are "live" and will always reflect the latest state. | ||
const tasksResults = realm.objects('Task'); | ||
if (tasksResults?.length) | ||
if (tasksResults?.length) { | ||
setTasks(tasksResults); | ||
} | ||
// Live queries and objects emit notifications when something has changed that we can listen for. | ||
@@ -66,4 +60,3 @@ subscriptionRef.current = tasksResults; | ||
}); | ||
} | ||
catch (err) { | ||
} catch (err) { | ||
console.error('Error opening realm: ', err.message); | ||
@@ -86,51 +79,68 @@ } | ||
const handleAddTask = useCallback((description) => { | ||
if (!description) | ||
return; | ||
useEffect(() => { | ||
openRealm(); | ||
// Everything in the function passed to "realm.write" is a transaction and will | ||
// hence succeed or fail together. A transcation is the smallest unit of transfer | ||
// in Realm so we want to be mindful of how much we put into one single transaction | ||
// and split them up if appropriate (more commonly seen server side). Since clients | ||
// may occasionally be online during short time spans we want to increase the probability | ||
// of sync participants to successfully sync everything in the transaction, otherwise | ||
// no changes propagate and the transaction needs to start over when connectivity allows. | ||
const realm = realmRef.current; | ||
realm?.write(() => { | ||
realm?.create('Task', new Task({ description })); | ||
}); | ||
}, [realmRef]); | ||
// Return a cleanup callback to close the realm to prevent memory leaks | ||
return closeRealm; | ||
}, [openRealm, closeRealm]); | ||
const handleToggleTaskStatus = useCallback((task) => { | ||
const realm = realmRef.current; | ||
realm?.write(() => { | ||
// Normally when updating a record in a NoSQL or SQL database, we have to type | ||
// a statement that will later be interpreted and used as instructions for how | ||
// to update the record. But in RealmDB, the objects are "live" because they are | ||
// actually referencing the object's location in memory on the device (memory mapping). | ||
// So rather than typing a statement, we modify the object directly by changing | ||
// the property values. If the changes adhere to the schema, Realm will accept | ||
// this new version of the object and wherever this object is being referenced | ||
// locally will also see the changes "live". | ||
task.isComplete = !task.isComplete; | ||
}); | ||
const handleAddTask = useCallback( | ||
description => { | ||
if (!description) { | ||
return; | ||
} | ||
// Alternatively if passing the ID as the argument to handleToggleTaskStatus: | ||
// realm?.write(() => { | ||
// const task = realm?.objectForPrimaryKey('Task', id); // If the ID is passed as an ObjectId | ||
// const task = realm?.objectForPrimaryKey('Task', Realm.BSON.ObjectId(id)); // If the ID is passed as a string | ||
// task.isComplete = !task.isComplete; | ||
// }); | ||
}, [realmRef]); | ||
// Everything in the function passed to "realm.write" is a transaction and will | ||
// hence succeed or fail together. A transcation is the smallest unit of transfer | ||
// in Realm so we want to be mindful of how much we put into one single transaction | ||
// and split them up if appropriate (more commonly seen server side). Since clients | ||
// may occasionally be online during short time spans we want to increase the probability | ||
// of sync participants to successfully sync everything in the transaction, otherwise | ||
// no changes propagate and the transaction needs to start over when connectivity allows. | ||
const realm = realmRef.current; | ||
realm?.write(() => { | ||
realm?.create('Task', new Task({description})); | ||
}); | ||
}, | ||
[realmRef], | ||
); | ||
const handleDeleteTask = useCallback((task) => { | ||
const realm = realmRef.current; | ||
realm?.write(() => { | ||
realm?.delete(task); | ||
const handleToggleTaskStatus = useCallback( | ||
task => { | ||
const realm = realmRef.current; | ||
realm?.write(() => { | ||
// Normally when updating a record in a NoSQL or SQL database, we have to type | ||
// a statement that will later be interpreted and used as instructions for how | ||
// to update the record. But in RealmDB, the objects are "live" because they are | ||
// actually referencing the object's location in memory on the device (memory mapping). | ||
// So rather than typing a statement, we modify the object directly by changing | ||
// the property values. If the changes adhere to the schema, Realm will accept | ||
// this new version of the object and wherever this object is being referenced | ||
// locally will also see the changes "live". | ||
task.isComplete = !task.isComplete; | ||
}); | ||
// Alternatively if passing the ID as the argument to handleDeleteTask: | ||
// realm?.delete(realm?.objectForPrimaryKey('Task', id)); | ||
}); | ||
}, [realmRef]); | ||
// Alternatively if passing the ID as the argument to handleToggleTaskStatus: | ||
// realm?.write(() => { | ||
// const task = realm?.objectForPrimaryKey('Task', id); // If the ID is passed as an ObjectId | ||
// const task = realm?.objectForPrimaryKey('Task', Realm.BSON.ObjectId(id)); // If the ID is passed as a string | ||
// task.isComplete = !task.isComplete; | ||
// }); | ||
}, | ||
[realmRef], | ||
); | ||
const handleDeleteTask = useCallback( | ||
task => { | ||
const realm = realmRef.current; | ||
realm?.write(() => { | ||
realm?.delete(task); | ||
// Alternatively if passing the ID as the argument to handleDeleteTask: | ||
// realm?.delete(realm?.objectForPrimaryKey('Task', id)); | ||
}); | ||
}, | ||
[realmRef], | ||
); | ||
return ( | ||
@@ -140,12 +150,11 @@ <SafeAreaView style={styles.screen}> | ||
<AddTaskForm onSubmit={handleAddTask} /> | ||
{(tasks.length === 0) | ||
? <IntroText /> | ||
: ( | ||
<TaskList | ||
tasks={tasks} | ||
onToggleTaskStatus={handleToggleTaskStatus} | ||
onDeleteTask={handleDeleteTask} | ||
/> | ||
) | ||
} | ||
{tasks.length === 0 ? ( | ||
<IntroText /> | ||
) : ( | ||
<TaskList | ||
tasks={tasks} | ||
onToggleTaskStatus={handleToggleTaskStatus} | ||
onDeleteTask={handleDeleteTask} | ||
/> | ||
)} | ||
</View> | ||
@@ -159,3 +168,3 @@ </SafeAreaView> | ||
flex: 1, | ||
backgroundColor: colors.darkBlue | ||
backgroundColor: colors.darkBlue, | ||
}, | ||
@@ -165,6 +174,6 @@ content: { | ||
paddingTop: 20, | ||
paddingHorizontal: 20 | ||
} | ||
paddingHorizontal: 20, | ||
}, | ||
}); | ||
export default App; |
@@ -1,7 +0,14 @@ | ||
import React, { useState } from 'react'; | ||
import { View, Text, TextInput, Pressable, Platform, StyleSheet } from 'react-native'; | ||
import React, {useState} from 'react'; | ||
import { | ||
View, | ||
Text, | ||
TextInput, | ||
Pressable, | ||
Platform, | ||
StyleSheet, | ||
} from 'react-native'; | ||
import colors from '../styles/colors'; | ||
function AddTaskForm({ onSubmit }) { | ||
function AddTaskForm({onSubmit}) { | ||
const [description, setDescription] = useState(''); | ||
@@ -18,15 +25,10 @@ | ||
value={description} | ||
placeholder='Enter new task description' | ||
placeholder="Enter new task description" | ||
onChangeText={setDescription} | ||
autoCorrect={false} | ||
autoCapitalize='none' | ||
autoCapitalize="none" | ||
style={styles.textInput} | ||
/> | ||
<Pressable | ||
onPress={handleSubmit} | ||
style={styles.submit} | ||
> | ||
<Text style={styles.icon}> | ||
+ | ||
</Text> | ||
<Pressable onPress={handleSubmit} style={styles.submit}> | ||
<Text style={styles.icon}>+</Text> | ||
</Pressable> | ||
@@ -47,11 +49,11 @@ </View> | ||
width: 0, | ||
height: 4 | ||
height: 4, | ||
}, | ||
shadowOpacity: 0.7, | ||
shadowRadius: 3 | ||
shadowRadius: 3, | ||
}, | ||
android: { | ||
elevation: 3 | ||
} | ||
}) | ||
elevation: 3, | ||
}, | ||
}), | ||
}, | ||
@@ -79,6 +81,6 @@ textInput: { | ||
fontSize: 17, | ||
fontWeight: 'bold' | ||
} | ||
fontWeight: 'bold', | ||
}, | ||
}); | ||
export default AddTaskForm; |
import React from 'react'; | ||
import { View, Text, Pressable, StyleSheet } from 'react-native'; | ||
import {View, Text, Pressable, StyleSheet} from 'react-native'; | ||
import openURLInBrowser from 'react-native/Libraries/Core/Devtools/openURLInBrowser'; // TODO: Replace library | ||
@@ -14,3 +14,5 @@ | ||
<Text style={styles.paragraph}> | ||
Start adding a task using the form at the top of the screen to see how they are created in Realm. You can also toggle the task status or remove it from the list. | ||
Start adding a task using the form at the top of the screen to see how | ||
they are created in Realm. You can also toggle the task status or remove | ||
it from the list. | ||
</Text> | ||
@@ -20,3 +22,6 @@ <Text style={styles.paragraph}> | ||
</Text> | ||
<Pressable onPress={() => openURLInBrowser('https://docs.mongodb.com/realm/sdk/react-native/')}> | ||
<Pressable | ||
onPress={() => | ||
openURLInBrowser('https://docs.mongodb.com/realm/sdk/react-native/') | ||
}> | ||
<Text style={[styles.paragraph, styles.link]}> | ||
@@ -34,3 +39,3 @@ docs.mongodb.com/realm/sdk/react-native | ||
marginHorizontal: 20, | ||
justifyContent: 'center' | ||
justifyContent: 'center', | ||
}, | ||
@@ -42,10 +47,10 @@ paragraph: { | ||
fontSize: 17, | ||
fontWeight: '500' | ||
fontWeight: '500', | ||
}, | ||
link: { | ||
color: colors.purple, | ||
fontWeight: 'bold' | ||
} | ||
fontWeight: 'bold', | ||
}, | ||
}); | ||
export default IntroText; |
@@ -1,7 +0,7 @@ | ||
import React, { memo } from 'react'; | ||
import { View, Text, Pressable, Platform, StyleSheet } from 'react-native'; | ||
import React, {memo} from 'react'; | ||
import {View, Text, Pressable, Platform, StyleSheet} from 'react-native'; | ||
import colors from '../styles/colors'; | ||
function TaskItem({ description, isComplete, onToggleStatus, onDelete }) { | ||
function TaskItem({description, isComplete, onToggleStatus, onDelete}) { | ||
return ( | ||
@@ -11,23 +11,12 @@ <View style={styles.task}> | ||
onPress={onToggleStatus} | ||
style={[styles.status, isComplete && styles.completed]} | ||
> | ||
<Text style={styles.icon}> | ||
{isComplete ? '✓' : '○'} | ||
</Text> | ||
style={[styles.status, isComplete && styles.completed]}> | ||
<Text style={styles.icon}>{isComplete ? '✓' : '○'}</Text> | ||
</Pressable> | ||
<View style={styles.descriptionContainer}> | ||
<Text | ||
numberOfLines={1} | ||
style={styles.description} | ||
> | ||
<Text numberOfLines={1} style={styles.description}> | ||
{description} | ||
</Text> | ||
</View> | ||
<Pressable | ||
onPress={onDelete} | ||
style={styles.deleteButton} | ||
> | ||
<Text style={styles.deleteText}> | ||
Delete | ||
</Text> | ||
<Pressable onPress={onDelete} style={styles.deleteButton}> | ||
<Text style={styles.deleteText}>Delete</Text> | ||
</Pressable> | ||
@@ -51,11 +40,11 @@ </View> | ||
width: 0, | ||
height: 4 | ||
height: 4, | ||
}, | ||
shadowOpacity: 0.7, | ||
shadowRadius: 3 | ||
shadowRadius: 3, | ||
}, | ||
android: { | ||
elevation: 3 | ||
} | ||
}) | ||
elevation: 3, | ||
}, | ||
}), | ||
}, | ||
@@ -69,3 +58,3 @@ descriptionContainer: { | ||
color: colors.black, | ||
fontSize: 17 | ||
fontSize: 17, | ||
}, | ||
@@ -78,6 +67,6 @@ status: { | ||
borderBottomLeftRadius: 5, | ||
backgroundColor: colors.gray | ||
backgroundColor: colors.gray, | ||
}, | ||
completed: { | ||
backgroundColor: colors.purple | ||
backgroundColor: colors.purple, | ||
}, | ||
@@ -90,3 +79,3 @@ deleteButton: { | ||
color: colors.gray, | ||
fontSize: 17 | ||
fontSize: 17, | ||
}, | ||
@@ -97,12 +86,11 @@ icon: { | ||
fontSize: 17, | ||
fontWeight: 'bold' | ||
} | ||
fontWeight: 'bold', | ||
}, | ||
}); | ||
// We want to make sure only tasks that change are rerendered | ||
const shouldNotRerender = (prevProps, nextProps) => ( | ||
prevProps.description === nextProps.description | ||
&& prevProps.isComplete === nextProps.isComplete | ||
); | ||
const shouldNotRerender = (prevProps, nextProps) => | ||
prevProps.description === nextProps.description && | ||
prevProps.isComplete === nextProps.isComplete; | ||
export default memo(TaskItem, shouldNotRerender); |
import React from 'react'; | ||
import { View, FlatList, StyleSheet } from 'react-native'; | ||
import {View, FlatList, StyleSheet} from 'react-native'; | ||
import TaskItem from './TaskItem'; | ||
function TaskList({ tasks, onToggleTaskStatus, onDeleteTask }) { | ||
function TaskList({tasks, onToggleTaskStatus, onDeleteTask}) { | ||
return ( | ||
@@ -11,4 +11,4 @@ <View style={styles.listContainer}> | ||
data={tasks} | ||
keyExtractor={(task) => task._id.toString()} | ||
renderItem={({ item }) => ( | ||
keyExtractor={task => task._id.toString()} | ||
renderItem={({item}) => ( | ||
<TaskItem | ||
@@ -30,6 +30,6 @@ description={item.description} | ||
flex: 1, | ||
justifyContent: 'center' | ||
} | ||
justifyContent: 'center', | ||
}, | ||
}); | ||
export default TaskList; |
@@ -1,5 +0,5 @@ | ||
import { BSON } from 'realm'; | ||
import {BSON} from 'realm'; | ||
class Task { | ||
constructor({ id = new BSON.ObjectId(), description, isComplete = false }) { | ||
constructor({id = new BSON.ObjectId(), description, isComplete = false}) { | ||
this._id = id; | ||
@@ -17,4 +17,4 @@ this.description = description; | ||
description: 'string', | ||
isComplete: { type: 'bool', default: false } | ||
} | ||
isComplete: {type: 'bool', default: false}, | ||
}, | ||
}; | ||
@@ -21,0 +21,0 @@ } |
const colors = { | ||
darkBlue : '#2A3642', | ||
purple : '#6E60F9', | ||
gray : '#B5B5B5', | ||
darkBlue: '#2A3642', | ||
purple: '#6E60F9', | ||
gray: '#B5B5B5', | ||
white: '#FFFFFF', | ||
black: '#000000' | ||
black: '#000000', | ||
}; | ||
export default colors; |
@@ -14,3 +14,3 @@ { | ||
"react": "17.0.2", | ||
"react-native": "0.65.1", | ||
"react-native": "0.66.1", | ||
"react-native-get-random-values": "^1.7.0", | ||
@@ -26,4 +26,3 @@ "realm": "^10.7.0" | ||
"jest": "^26.6.3", | ||
"metro-react-native-babel-preset": "^0.66.0", | ||
"react-native-codegen": "^0.0.7", | ||
"metro-react-native-babel-preset": "^0.66.2", | ||
"react-test-renderer": "17.0.2" | ||
@@ -34,2 +33,2 @@ }, | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
234255
668