Skip to content

Instantly share code, notes, and snippets.

@SimeonGriggs
Last active December 8, 2021 10:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save SimeonGriggs/0a1e1a6d86af70ba7ee863f9bb5dfaf9 to your computer and use it in GitHub Desktop.
Save SimeonGriggs/0a1e1a6d86af70ba7ee863f9bb5dfaf9 to your computer and use it in GitHub Desktop.
A "listening" version of usePreviewSubscription() that re-queries the client. Not yet recommended for Production use.
const { data, loading } = useListeningQuery(
query,
{
params: params ?? {},
initialData: page,
enabled: preview,
delay: 250,
}
);
import {createClient} from 'next-sanity'
import {config} from './config'
// Set up the client for fetching data in the getProps page functions
export const sanityClient = createClient(config)
// Listener client uses credentials to fetch drafts as it's used client-side
export const listenerClient = createClient({
...config,
useCdn: false,
withCredentials: true
})
// Set up a preview client with serverless authentication for drafts
export const previewClient = createClient({
...config,
useCdn: false,
token: process.env.SANITY_API_TOKEN,
})
// Helper function for easily switching between normal client and preview client
export const getClient = (usePreview) => usePreview ? previewClient : sanityClient
import { useEffect, useState } from "react";
import { extract } from "@sanity/mutator";
import debounce from "lodash/debounce";
import { listenerClient } from "../lib/sanity.server";
const useListeningQuery = (query, config) => {
const { params = {}, initialData, enabled = false, delay = 1000 } = config;
const [data, setData] = useState(initialData);
const [loading, setLoading] = useState(false);
// Find the ids of documents we want to listen to changes on
// Maybe: Should include draft.ids of each as well?
const references = extract(`.._ref`, initialData);
const ids = extract(`.._id`, initialData);
const docIds = Array.from(new Set([...references, ...ids])).filter(
(id) => !id.startsWith("image-") && !id.startsWith("file-")
);
function fetchData() {
// This isn't a great place to put this because it only triggers when fetch starts,
// Not when listener starts receiving updates
// Perhaps we could have isListening + isLoading
setLoading(true)
// Listener client uses credentials to fetch draft content
listenerClient
.fetch(query, params)
.then((newData) => {
setData(newData)
setLoading(false)
});
}
useEffect(() => {
let subscription;
if (enabled) {
// Do we even need to fetch immediately if we already have data?
// Probably not
// fetchData();
// We're not listening to the passed-in query
// We listen for changes on any of the initialData's _id's and _ref's
subscription = listenerClient
.listen(`*[_id in $docIds]`, { docIds }, { includeResult: false })
.subscribe(
debounce(() => {
// console.log(`Debounced fetch`)
fetchData()
// For some reason it helps to have a delayed additional fetch
// The last keystroke doesn't always trigger a refetch
// TODO: Understand why and fix this
setTimeout(() => {
// console.log(`Debounced extra fetch`)
fetchData()
}, delay);
}, delay)
);
}
return () => {
if (subscription) {
subscription.unsubscribe();
}
};
}, [initialData]);
return { data, loading };
};
export default useListeningQuery;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment