react-ketting
Version:
Ketting bindings for React
117 lines • 4.67 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePagedCollection = exports.useInfiniteCollection = void 0;
const react_1 = require("react");
const use_read_resource_1 = require("./use-read-resource");
/**
* The useInfiniteCollection hook works similar to useCollection, but has the
* ability to load in additional pages of items.
*
* For this to work, the API needs to expose a "next" link on the collection.
* As long as there are "next" links, more pages can be loaded in.
*
* Additional items from collection pages will be appended to "items",
* allowing frontends to build 'infinite scroll' features.
*
* Example call:
*
* <pre>
* const {
* loading,
* error,
* items,
* } = useInfiniteResource<Article>(resource);
* </pre>
*
* The resource may be passed as a Resource object, a Promise<Resource>, or a
* uri string.
*
* Returned properties:
*
* * loading - will be true every time we're going to the server and fetch a
* a new page.
* * error - Will be null or an error object.
* * items - Will contain an array of resources, each typed Resource<T> where
* T is the passed generic argument.
* * hasNextPage - Will be true if the server has another page.
* * loadNextPage - Loads the next page, and appends the new items to the
* items array.
*/
function useInfiniteCollection(resourceLike, options) {
var _a, _b;
const rel = (options === null || options === void 0 ? void 0 : options.rel) || 'item';
const [items, setItems] = (0, react_1.useState)([]);
// Are there more pages?
const nextPageResource = (0, react_1.useRef)(null);
const error = (0, react_1.useRef)(null);
// Are we currently loading a 'next page'. This is used to avoid race conditions
const [loading, setLoading] = (0, react_1.useState)(false);
// This is the 'base collection'
const bc = (0, use_read_resource_1.useReadResource)(resourceLike, {
refreshOnStale: options === null || options === void 0 ? void 0 : options.refreshOnStale,
// This header will be included on the first, uncached fetch.
// This may be helpful to the server and instruct it to embed
// all collection members in that initial fetch.
initialGetRequestHeaders: {
Prefer: 'transclude=' + rel,
}
});
(0, react_1.useEffect)(() => {
if (!bc.loading) {
// The 'base collection' has stopped loading, so lets set the first page.
setItems(bc.resourceState.followAll(rel));
nextPageResource.current = bc.resourceState.links.has('next') ? bc.resourceState.follow('next') : null;
setLoading(false);
}
}, [bc.resourceState]);
let loadNextPageCalled = false;
const loadNextPage = async () => {
if (!nextPageResource.current) {
console.warn('loadNextPage was called, but there was no next page');
return;
}
if (loadNextPageCalled) {
console.warn('You called loadNextPage(), but it was an old copy. You should not memoize or store a reference to this function, but instead always use the one that was returned last. We ignored this call');
return;
}
loadNextPageCalled = true;
// We are currently loading a new page
setLoading(true);
try {
const nextPageState = await nextPageResource.current.get({
headers: {
Prefer: 'transclude=' + rel,
}
});
// Set up the next page.
nextPageResource.current = nextPageState.links.has('next') ? nextPageState.follow('next') : null;
// Add new resources to page data
setItems([
...items,
...nextPageState.followAll(rel)
]);
}
catch (err) {
error.current = err;
}
setLoading(false);
};
return {
loading: bc.loading || loading,
error: (_b = (_a = bc.error) !== null && _a !== void 0 ? _a : error.current) !== null && _b !== void 0 ? _b : null,
items,
hasNextPage: nextPageResource.current !== null,
loadNextPage,
};
}
exports.useInfiniteCollection = useInfiniteCollection;
/**
* usePagedCollection is the deprecated old name for useInfiniteCollection
*
* @deprecated Rename to useInfiniteCollection
*/
function usePagedCollection(resourceLike, options) {
return useInfiniteCollection(resourceLike, options);
}
exports.usePagedCollection = usePagedCollection;
//# sourceMappingURL=use-infinite-collection.js.map