Skip to content

Instantly share code, notes, and snippets.

@dr-skot
Last active May 16, 2022 12:17
Show Gist options
  • Save dr-skot/0fbdb95b4415e29be5b1beb73e8b2045 to your computer and use it in GitHub Desktop.
Save dr-skot/0fbdb95b4415e29be5b1beb73e8b2045 to your computer and use it in GitHub Desktop.
Utilities for using the Parse Platform with nextjs. Prefer this to @parse/react-ssr
import BrowserParse from 'parse';
import ServerParse from 'parse/node';
import _ from 'lodash';
const isBrowser = !!global.window;
const Parse = isBrowser ? BrowserParse : ServerParse;
export default Parse;
export function initialize(serverURL: string, applicationId: string, javascriptKey: string): void {
Parse.serverURL = serverURL;
Parse.initialize(applicationId, javascriptKey);
if (isBrowser) Parse.enableLocalDatastore();
}
// useful for relationship querying
export function stub(className: string, id?: string): Parse.Object | undefined {
if (!id) return undefined;
const stub = new Parse.Object(className);
stub.id = id;
return stub;
}
// converts parse object to plain-old-javascript-object
export function pojo(parseObject?: Parse.Object): unknown {
if (!parseObject) return undefined;
return _.mapValues({ id: parseObject.id, ...parseObject.attributes }, (value: unknown) =>
value instanceof Parse.Object
? pojo(value)
: _.isDate(value)
? (value as Date).getTime()
: value
);
}
export { useLiveQuery } from './useLiveQuery'
import Parse from './Parse';
import { useEffect, useRef, useState } from 'react';
const RETRY_INTERVAL = 2000;
export function useLiveQuery(query: Parse.Query): { results?: Parse.Object[]; isLive: boolean } {
const subscriptionRef = useRef<{ unsubscribe: () => void }>();
const [results, setResults] = useState<Parse.Object[]>();
const [isLive, setIsLive] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [retries, setRetries] = useState(0);
const encodedQuery = encodeQuery(query);
useEffect(() => {
if (!navigator.onLine) return;
// (re)load the results
setIsLoading(true);
query.find().then((result) => {
setResults(result);
setIsLoading(false);
});
// handle add/update/remove
const add = (object: Parse.Object) => setResults((prevs) => [...(prevs || []), object]);
const update = (object: Parse.Object) =>
setResults((prevs) => (prevs || []).map((prev) => (prev.id === object.id ? object : prev)));
const remove = (object: Parse.Object) =>
setResults((prevs) => (prevs || []).filter((prev) => prev.id !== object.id));
const open = () => setIsLive(true);
const close = () => setIsLive(false);
// subscribe and listen for add/update/remove events
query.subscribe().then((subscription) => {
subscriptionRef.current = subscription;
subscription.on('open', open);
subscription.on('create', add);
subscription.on('update', update);
subscription.on('enter', add);
subscription.on('leave', remove);
subscription.on('delete', remove);
subscription.on('close', close);
window.addEventListener('offline', close);
});
return () => {
subscriptionRef.current?.unsubscribe();
window.removeEventListener('offline', close);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [encodedQuery, retries]);
// retry until isLive
useEffect(() => {
if (isLive) return;
const retry = () => setRetries((prev) => prev + 1);
const interval = window.setInterval(retry, RETRY_INTERVAL);
return () => window.clearInterval(interval);
}, [isLive]);
return { results, isLive: isLive && !isLoading };
}
function encodeQuery(query: Parse.Query): string {
const encoded = {
className: query.className,
query: query.toJSON(),
};
return JSON.stringify(encoded);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment