-
Notifications
You must be signed in to change notification settings - Fork 55
/
Copy pathuseDocument.ts
63 lines (55 loc) · 1.77 KB
/
useDocument.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import { AnyDocumentId, DocHandle } from "@automerge/automerge-repo/slim"
import { ChangeFn, ChangeOptions, Doc } from "@automerge/automerge/slim/next"
import { useCallback, useEffect, useRef, useState } from "react"
import { useRepo } from "./useRepo.js"
/** A hook which returns a document identified by a URL and a function to change the document.
*
* @returns a tuple of the document and a function to change the document.
* The document will be `undefined` if the document is not available in storage or from any peers
*
* @remarks
* This requires a {@link RepoContext} to be provided by a parent component.
* */
export function useDocument<T>(
id?: AnyDocumentId
): [
Doc<T> | undefined,
(changeFn: ChangeFn<T>, options?: ChangeOptions<T> | undefined) => void
] {
const repo = useRepo()
const handle = id ? repo.find<T>(id) : null
const handleRef = useRef<DocHandle<T> | null>(handle)
if (handle !== handleRef.current) {
handleRef.current = handle
}
// a state value we use to trigger a re-render
const [, setGeneration] = useState(0)
const rerender = () => setGeneration(v => v + 1)
useEffect(() => {
if (!id || !handle) {
return
}
handleRef.current = handle
handle
.doc()
.then(() => {
rerender()
})
.catch(e => console.error(e))
handle.on("change", rerender)
handle.on("delete", rerender)
const cleanup = () => {
handle.removeListener("change", rerender)
handle.removeListener("delete", rerender)
}
return cleanup
}, [id, handle])
const changeDoc = useCallback(
(changeFn: ChangeFn<T>, options?: ChangeOptions<T> | undefined) => {
if (!handle) return
handle.change(changeFn, options)
},
[handle]
)
return [handle?.docSync(), changeDoc] as const
}