haystack-react
Version:
Project Haystack utilities for building React applications
200 lines • 6.04 kB
JavaScript
/*
* Copyright (c) 2020, J2 Innovations. All Rights Reserved
*/
/* eslint prefer-const: "off" */
import { useState, useEffect, createContext, useContext, useRef } from 'react';
import { Client } from 'haystack-nclient';
import { HGrid, HRef, HFilterBuilder } from 'haystack-core';
/**
* A hook that asynchronously resolves to a grid.
*
* @param options.getGrid An asynchronous method that resolves to a grid.
* @param options.dependencies The dependencies that cause the grid to be refetched.
* @returns A result that wraps the data.
*/
export function useGrid({ getGrid, dependencies, }) {
const data = useRef();
const gridData = data.current ??
(data.current = { grid: new HGrid(), isLoading: true, loads: 0 });
let [updates, setUpdates] = useState(0);
const forceUpdate = () => setUpdates(++updates);
useEffect(() => {
let cancel = false;
async function doGetGrid() {
try {
const wasLoading = gridData.isLoading;
gridData.isLoading = true;
// If a grid wasn't loading before it is now so force an
// update in the UI.
if (!wasLoading) {
forceUpdate();
}
const grid = await getGrid();
if (!cancel) {
gridData.grid = grid;
}
}
catch (err) {
if (!cancel) {
gridData.error = err;
}
}
finally {
if (!cancel) {
++gridData.loads;
gridData.isLoading = false;
}
// Force an update after everything has completed.
forceUpdate();
}
}
doGetGrid();
return () => {
cancel = true;
gridData.isLoading = false;
gridData.error = undefined;
};
}, dependencies // Effect re-runs if any of these parameters change.
);
return gridData;
}
/**
* The client context to be used.
*/
export const ClientContext = createContext(new Client({
base: new URL(globalThis?.location?.href || 'http://localhost'),
}));
ClientContext.displayName = 'Client';
/**
* @returns The client to use for network communication.
*/
export function useClient() {
return useContext(ClientContext);
}
/**
* A hook that restricts network communcations only to ops.
*/
export const OnlyOpsContext = createContext(true);
OnlyOpsContext.displayName = 'OnlyOps';
/**
* @returns true if only ops should be used for network communication.
*/
export function useOnlyOps() {
return useContext(OnlyOpsContext);
}
/**
* A hook that resolves a some ids into a grid.
*
* @param ids The ids to read from the server.
* @returns A result that wraps the grid.
*/
export function useReadByIds(ids) {
const client = useClient();
const ops = useOnlyOps();
const [refreshes, setRefreshes] = useState(0);
if (ops) {
let builder;
for (const id of ids) {
if (!builder) {
builder = new HFilterBuilder();
}
else {
builder.or();
}
builder.equals('id', HRef.make(id));
}
return useReadByFilter(builder?.build() ?? '');
}
else {
const result = useGrid({
getGrid: async () => {
return ids.length ? client.record.readByIds(ids) : new HGrid();
},
dependencies: [JSON.stringify(ids), client, ops, refreshes],
});
return {
...result,
refresh: () => setRefreshes(refreshes + 1),
};
}
}
/**
* A hook that resolves an id into a grid.
*
* @param id The id to read to read from the server.
* @returns A result that wraps the grid.
*/
export function useReadById(id) {
const client = useClient();
const ops = useOnlyOps();
const [refreshes, setRefreshes] = useState(0);
const dependencies = [JSON.stringify(id), client, ops, refreshes];
let result;
if (ops) {
const filter = new HFilterBuilder().equals('id', HRef.make(id)).build();
result = useGrid({
getGrid: async () => String(id) ? client.ops.read(filter) : new HGrid(),
dependencies,
});
}
else {
result = useGrid({
getGrid: async () => String(id)
? (await client.record.readById(id)).toGrid()
: new HGrid(),
dependencies,
});
}
return {
...result,
refresh: () => setRefreshes(refreshes + 1),
};
}
/**
* A hook that resolves a haystack filter into a grid.
*
* @param filter The haystack filter to query from the server.
* @returns A result that wraps the grid.
*/
export function useReadByFilter(filter) {
const client = useClient();
const ops = useOnlyOps();
const [refreshes, setRefreshes] = useState(0);
const dependencies = [filter, client, ops, refreshes];
let result;
if (ops) {
result = useGrid({
getGrid: async () => filter ? client.ops.read(filter) : new HGrid(),
dependencies,
});
}
else {
result = useGrid({
getGrid: async () => filter ? client.record.readByFilter(filter) : new HGrid(),
dependencies,
});
}
return {
...result,
refresh: () => setRefreshes(refreshes + 1),
};
}
/**
* A hook that responses an expression into a grid.
*
* @param expr The expression to evalulate.
* @returns The result that wraps the grid.
*/
export function useEval(expr) {
const client = useClient();
const [refreshes, setRefreshes] = useState(0);
const result = useGrid({
getGrid: async () => client.ext.eval(expr),
dependencies: [expr, client, refreshes],
});
return {
...result,
refresh: () => setRefreshes(refreshes + 1),
};
}
//# sourceMappingURL=client.js.map