UNPKG

haystack-react

Version:

Project Haystack utilities for building React applications

200 lines 6.04 kB
/* * 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