Skip to content

Commit

Permalink
fix async bug in useDocument
Browse files Browse the repository at this point in the history
previously useDocument did not behave well for documents that took a long time to load:

- while a load was blocking the UI, an old stale doc would be shown
- loads that finished out of order could result in an inconsistent final state

This change fixes those two issues:

- Upon a handle changing, the old doc contents are cleared out to avoid staleness inconsistencies.
- Out-of-order loads are accounted for; when a load finishes it's ignored if the handle has since changed.
  • Loading branch information
geoffreylitt committed Dec 12, 2023
1 parent 2d6e23b commit 798fbe9
Showing 1 changed file with 22 additions and 7 deletions.
29 changes: 22 additions & 7 deletions packages/automerge-repo-react-hooks/src/useDocument.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChangeFn, ChangeOptions, Doc } from "@automerge/automerge/next"
import { AutomergeUrl, DocHandleChangePayload } from "@automerge/automerge-repo"
import { useEffect, useState } from "react"
import { useLayoutEffect, 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.
Expand All @@ -19,16 +19,31 @@ export function useDocument<T>(

const handle = documentUrl ? repo.find<T>(documentUrl) : null

useEffect(() => {
if (!handle) {
if (doc) {
setDoc(undefined)
}
const handleRef = useRef(null)

// Doing this in a useLayoutEffect seems to help make sure that
// a loading state gets rendered before we block the UI thread
// with a slow load.
useLayoutEffect(() => {
// When the handle has changed, reset the doc to an empty state.
// This ensures that if loading the doc takes a long time, the UI
// shows a loading state during that time rather than a stale doc.
setDoc(undefined)

if (!handle) {
return
}

handle.doc().then(v => setDoc(v))
handleRef.current = handle
handle.doc().then(v => {
// Bail out on updating the doc if the handle has changed since we started loading.
// This avoids problem with out-of-order loads when the handle is changing faster
// than documents are loading.
if (handleRef.current !== handle) {
return
}
setDoc(v)
})

const onChange = (h: DocHandleChangePayload<T>) => setDoc(h.doc)
handle.on("change", onChange)
Expand Down

0 comments on commit 798fbe9

Please sign in to comment.