haystack-react
Version:
Project Haystack utilities for building React applications
117 lines • 4.36 kB
JavaScript
/*
* Copyright (c) 2020, J2 Innovations. All Rights Reserved
*/
/* eslint prefer-const: "off" */
import { useEffect, useRef, useState } from 'react';
import { DEFAULT_POLL_RATE_SECS } from 'haystack-nclient';
import { HGrid } from 'haystack-core';
import { useClient, useOnlyOps } from './client';
/**
* A hook to watch some live haystack data.
*
* @param options.ids The ids of the records to watch. Alternatively a `filter` can be specified.
* @param options.filter The haystack filter to query on the server. Alternatively record `ids`
* can be specified.
* @param options.display An optional display name for the filter.
* @param options.pollRate An optional poll rate for the watch in seconds.
* @returns A result that wraps a grid.
*/
export function useWatch({ filter, ids, display, pollRate = DEFAULT_POLL_RATE_SECS, }) {
const client = useClient();
const ops = useOnlyOps();
const data = useRef();
const watchData = data.current ??
(data.current = {
grid: new HGrid(),
isLoading: true,
loads: 0,
});
const [refreshes, setRefreshes] = useState(0);
let [updates, setUpdates] = useState(0);
const forceUpdate = () => setUpdates(++updates);
useEffect(() => {
let cancel = false;
async function open() {
try {
const wasLoading = watchData.isLoading;
watchData.isLoading = true;
// If watch wasn't loading before it is now so force an
// update in the UI.
if (!wasLoading) {
forceUpdate();
}
if (!ids) {
ids = filter
? await (ops
? client.ops.read(filter)
: client.record.readByFilter(filter))
: new HGrid();
// If the effect was closed after the network call then bail.
if (cancel) {
return;
}
watchData.grid = ids;
watchData.isLoading = false;
// Force an update here as we already have some valid data to render before
// the watch is opened.
forceUpdate();
}
const watch = await (ops
? client.ops.watch
: client.watch).make(display ||
filter ||
`useWatch@${new Date().toISOString()}`, ids);
// If the effect was closed after opening the watch then close it and bail.
if (cancel) {
await watch.close();
return watch;
}
watch.pollRate =
pollRate > 0 ? pollRate : DEFAULT_POLL_RATE_SECS;
watch.changed({
callback: forceUpdate,
});
watchData.grid = watch.grid;
return watch;
}
catch (err) {
if (!cancel) {
watchData.error = err;
}
}
finally {
if (!cancel) {
watchData.isLoading = false;
++watchData.loads;
}
forceUpdate();
}
return undefined;
}
const promise = open();
// Return a function that will be invoked when the effect is closed.
return () => {
cancel = true;
// On clean up, reset to the initial state.
watchData.isLoading = false;
watchData.error = undefined;
async function close() {
if (promise) {
const watch = await promise;
watch?.close();
}
}
close();
};
}, [filter, JSON.stringify(ids), client, display, pollRate, ops, refreshes] // Effect re-runs if any of these parameters change.
);
return {
grid: watchData.grid,
isLoading: watchData.isLoading,
loads: watchData.loads,
error: watchData.error,
refresh: () => setRefreshes(refreshes + 1),
updates,
};
}
//# sourceMappingURL=watch.js.map