👋 Next.js Conf 2024: Come build, party, run, and connect with us! See all events

Discussion on fetching fresh data from API in custom document view component using useClient() and a reference implementation of useListeningQuery hook.

5 replies
Last updated: Apr 25, 2023
Hi Sanity Community — I’m trying to fetch fresh data from the API in a custom document view component with
useClient()
. I want the fetched data to match the timestamp of
document.displayed._updatedAt
, which the custom view component is supplied with from Structure Builder. If I delay the fetch, the data matches (i.e. the content lake has time to update), but I’d much rather avoid the arbitrary delay. I’ve tried adding the pause after an initial non-matching fetch and calling the function recursively, but even with a delay here, the values never match — in fact, they never even change (see image below the code snippet). Any ideas on why this isn’t working, or how I might otherwise eliminate the hard-coded delay? Thank you!

  const fetchConcepts = async (): Promise<any> => {
    try {
      // the current working version puts a delay here, which returns consistent results, but is not ideal:
      // await new Promise((resolve) => setTimeout(resolve, 1000))
      const res = await client.fetch(trunkBuilder(), {id: dId})
      // what I would like to do is check the _updatedAt value of the displayed document and the response, and if they don't match, wait a bit and try again:
      if (res._updatedAt !== document.displayed._updatedAt) {
        console.log('res._updatedAt', res._updatedAt)
        console.log('document.displayed._updatedAt', document.displayed._updatedAt)
        await new Promise((resolve) => setTimeout(resolve, 250))
        // unfortunately, this doesn't work. The fetched document is not updated, and the query returns the same results. Forever:
        return fetchConcepts()
      } else if (res.topConcepts === null && res.orphans.length < 1) {
        setNoConcept(true)
      } else {
        setNoConcept(false)
        setConcepts(res)
      }
    } catch (error) {
      // eslint-disable-next-line no-console
      console.log('Error: ', error)
      setIsError(true)
    }
    return setIsLoading(false)
  }
Apr 22, 2023, 5:10 PM
Have you tried querying from documentStore instead of the client? It could be ever so slightly up to date than the client.
I’ve got a reference implementation of it here in this useListeningQuery hook which you might find useful:

https://github.com/SimeonGriggs/sanity-plugin-utils#uselisteningquery
Apr 24, 2023, 8:05 AM
I haven’t tried that yet — but I will. That looks like it could replace my whole
useEffect()
hook — and maybe even all the fussy logic I’m using to keep
documentId
and
document.displayed._id
in sync. Thanks for the suggestion and the link!
Apr 24, 2023, 4:09 PM
I’ve built a lot of little plugins that needed querying with live updates and loading/error states – so I’ve used this on a number of projects. LMK if you have any feedback though!
Apr 24, 2023, 4:10 PM
Fantastic — thanks, I’ll do that!
Apr 24, 2023, 4:10 PM
So … that hook cut my fetch code down from 107 lines to 30 🤩There are a couple update quirks to work out, but I think those are in my components (which sorely need to be streamlined). I’ve also got some near term uses for a couple of these other hooks — thank you for putting these together!
Apr 25, 2023, 3:29 AM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?