Last active
March 6, 2022 19:44
-
-
Save mfbx9da4/4eede24c70d0669ddd5f96bdd2e8416d to your computer and use it in GitHub Desktop.
Github issue realm-js flatlist + listener
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useEffect, useLayoutEffect, useState } from 'react' | |
import { FlatList, SafeAreaView } from 'react-native' | |
import RNFS from 'react-native-fs' | |
import Realm from 'realm' | |
export function App() { | |
const r = useWaitForRealm() | |
const [initialized, setInitialized] = useState(false) | |
useEffect(() => { | |
if (!r) return | |
const asyncEffect = () => { | |
// Cleanup the db | |
const fooResults = realm.objects<Foo>('foo') | |
realm.write(() => { | |
for (const x of fooResults) { | |
realm.delete(x) | |
} | |
realm.create<Foo>('foo', { id: '-1' }, Realm.UpdateMode.Modified) | |
}) | |
setInitialized(true) | |
} | |
void asyncEffect() | |
}, [r]) | |
if (!initialized) return null | |
return <FooList /> | |
} | |
let i = 0 | |
const sleep = (milliseconds: number) => new Promise(r => setTimeout(r, milliseconds)) | |
function FooList() { | |
const fooResults = useQuery<Foo>(() => realm.objects<Foo>('foo')) | |
useEffect(() => { | |
const asyncEffect = async () => { | |
while (i < 30) { | |
const id = String(i++) | |
console.log('Start write 1, id:', id) | |
await sleep(10) | |
realm.write(() => { | |
realm.create<Foo>('foo', { id }, Realm.UpdateMode.Modified) | |
}) | |
await sleep(0) | |
console.log('start write 2, id:', id) | |
realm.write(() => { | |
realm.create<Foo>('foo', { id }, Realm.UpdateMode.Modified) | |
}) | |
} | |
} | |
asyncEffect().catch(console.error) | |
}, []) | |
return ( | |
<SafeAreaView style={{ margin: 20 }}> | |
<Text>{fooResults?.length}</Text> | |
{/* {fooResults?.map((_, index) => ( | |
<Message index={index} /> | |
))} */} | |
<FlatList | |
inverted | |
data={fooResults} | |
renderItem={x => <Message index={x.index} />} | |
keyExtractor={item => item.id} | |
maintainVisibleContentPosition={{ minIndexForVisible: 0, autoscrollToTopThreshold: 500 }} | |
/> | |
</SafeAreaView> | |
) | |
} | |
function Message({ index }: { index: number }) { | |
console.log('Message', index) | |
const x = useObject<Foo>('foo', '-1') | |
return <Text>{x?.id}</Text> | |
} | |
// #region === Setup the Realm instance (start) === | |
// You can skip reading this bit, I've left it here so it can be easily reproduced. | |
const FooSchema: Realm.ObjectSchema = { | |
name: 'foo', | |
primaryKey: 'id', | |
properties: { | |
id: 'string', | |
}, | |
} | |
export let realm: Realm | |
let realmInitializingPromise: Promise<Realm> | undefined | |
export function waitForRealm() { | |
if (realm) return Promise.resolve(realm) | |
if (!realmInitializingPromise) realmInitializingPromise = initRealm() | |
return realmInitializingPromise | |
} | |
async function initRealm() { | |
const path = `${RNFS.CachesDirectoryPath}/example.realm` | |
realm = await Realm.open({ | |
path, | |
schema: [FooSchema], | |
schemaVersion: 0, | |
}) | |
return realm | |
} | |
export function useWaitForRealm() { | |
const [optionalRealm, setRealm] = useState<Realm | undefined>(realm) | |
useEffect(() => { | |
waitForRealm() | |
.then(x => setRealm(x)) | |
.catch(console.error) | |
}, []) | |
return optionalRealm | |
} | |
type Foo = { id: string } | |
export function useObject<T>(type: string, primaryKey: string): (T & Realm.Object) | undefined { | |
const [object, setObject] = useState<(T & Realm.Object) | undefined>( | |
realm.objectForPrimaryKey(type, primaryKey) | |
) | |
useEffect(() => { | |
const listenerCallback: Realm.ObjectChangeCallback = (_, changes) => { | |
if (changes.changedProperties.length > 0) { | |
setObject(realm.objectForPrimaryKey(type, primaryKey)) | |
} else if (changes.deleted) { | |
setObject(undefined) | |
} | |
} | |
if (object !== undefined) { | |
object.addListener(listenerCallback) | |
} | |
return () => { | |
object?.removeListener(listenerCallback) | |
} | |
}, [object, type, primaryKey]) | |
return object | |
} | |
function useQuery<T>(query: () => Realm.Results<any>) { | |
const [collection, setCollection] = useState<Realm.Results<T>>(query()) | |
useEffect(() => { | |
const listenerCallback: Realm.CollectionChangeCallback<T> = (_, changes) => { | |
const { deletions, insertions, newModifications } = changes | |
if (deletions.length > 0 || insertions.length > 0 || newModifications.length > 0) { | |
setCollection(query()) | |
} | |
} | |
if (collection && collection.isValid() && !realm.isClosed) | |
collection.addListener(listenerCallback) | |
return () => { | |
collection?.removeListener(listenerCallback) | |
} | |
}, [collection]) | |
return collection | |
} | |
// #endregion === Setup the Realm instance (end) === |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment