Group context and hook creation so that it is exportable. (#6451)

CHANGELOG_BEGIN
Export Ledger context and hook creation to enable nested interaction,
with different parties or different ledgers, within one React app.
CHANGELOG_END

* Group context and hook creation so that it is exportable.

* Use undefined as default state to avoid cast

* Word choice

* Document new functions

* Revert commit to build script

* Test nesting of contexts

* Document extra feature in README

* Reorganize code to preserve individual function documentation.

* Correct names to starting with lowercase in build.

* Single quote imports and spacing style

* Add copyright notice

* Spacing around useReload

* Use a good variable name

* Do not export by default

Co-authored-by: Martin Huschenbett <martin.huschenbett@posteo.me>
This commit is contained in:
Leonid Rozenberg 2020-06-23 10:45:19 -04:00 committed by GitHub
parent 6bab178195
commit ea344518e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 353 additions and 265 deletions

View File

@ -13,9 +13,8 @@ load("@build_environment//:configuration.bzl", "sdk_version")
sources = [
"index.ts",
"index.test.ts",
"context.ts",
"hooks.ts",
"DamlLedger.ts",
"createLedgerContext.ts",
"defaultLedgerContext.ts",
]
da_ts_library(

View File

@ -1,28 +0,0 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useMemo, useState } from 'react';
import { DamlLedgerContext, DamlLedgerState } from './context';
import Ledger from '@daml/ledger';
import { Party } from '@daml/types';
type Props = {
token: string;
httpBaseUrl?: string;
wsBaseUrl?: string;
party: Party;
}
const DamlLedger: React.FC<Props> = ({token, httpBaseUrl, wsBaseUrl, party, children}) => {
const [reloadToken, setReloadToken] = useState(0);
const ledger = useMemo(() => new Ledger({token, httpBaseUrl, wsBaseUrl}), [token, httpBaseUrl, wsBaseUrl]);
const state: DamlLedgerState = useMemo(() => ({
reloadToken,
triggerReload: (): void => setReloadToken(x => x + 1),
party,
ledger,
}), [party, ledger, reloadToken]);
return React.createElement(DamlLedgerContext.Provider, {value: state}, children);
}
export default DamlLedger;

View File

@ -117,6 +117,15 @@ the result.
const {contract, loading} = useStreamFetchByKey(ContractTemplate, () => key, [dependency1, dependency2, ...]);
```
## Advanced Usage
In order to interact as multiple parties or to connect to several ledgers, one needs to create an extra
`DamlLedger` [contexts](https://reactjs.org/docs/context.html) specific to your requirement.
`createLedgerContext`
---------------------
`createLedgerContext` returns another `DamlLedger` context and associated hooks (`useParty`, `useLedger` ... etc)
that will look up their connection within that returned context.
## Source
https://github.com/digital-asset/daml.

View File

@ -1,15 +0,0 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import Ledger from '@daml/ledger';
import { Party } from '@daml/types';
export type DamlLedgerState = {
reloadToken: unknown;
triggerReload: () => void;
party: Party;
ledger: Ledger;
}
export const DamlLedgerContext = React.createContext(null as DamlLedgerState | null);

View File

@ -0,0 +1,203 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import React, {useContext, useEffect, useMemo, useState } from 'react';
import { Party, Template } from '@daml/types';
import Ledger, { CreateEvent, Query } from '@daml/ledger';
/**
* @internal
*/
type DamlLedgerState = {
reloadToken: unknown;
triggerReload: () => void;
party: Party;
ledger: Ledger;
}
/**
* React props to initiate a connect to a DAML ledger.
*/
export type LedgerProps = {
token: string;
httpBaseUrl?: string;
wsBaseUrl?: string;
party: Party;
}
/**
* The result of a ``query`` against the ledger.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*/
export type QueryResult<T extends object, K, I extends string> = {
/** Contracts matching the query. */
contracts: readonly CreateEvent<T, K, I>[];
/** Indicator for whether the query is executing. */
loading: boolean;
}
/**
* The result of a ``fetch`` against the ledger.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*/
export type FetchResult<T extends object, K, I extends string> = {
/** Contracts of the given contract template and key. */
contract: CreateEvent<T, K, I> | null;
/** Indicator for whether the fetch is executing. */
loading: boolean;
}
/**
* A LedgerContext is a React context that stores information about a DAML Ledger
* and hooks necessary to use it.
*/
export type LedgerContext = {
DamlLedger: React.FC<LedgerProps>;
useParty: () => Party;
useLedger: () => Ledger;
useQuery: <T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory?: () => Query<T>, queryDeps?: readonly unknown[]) => QueryResult<T, K, I>;
useFetchByKey: <T extends object, K, I extends string>(template: Template<T, K, I>, keyFactory: () => K, keyDeps: readonly unknown[]) => FetchResult<T, K, I>;
useStreamQuery: <T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory?: () => Query<T>, queryDeps?: readonly unknown[]) => QueryResult<T, K, I>;
useStreamFetchByKey: <T extends object, K, I extends string>(template: Template<T, K, I>, keyFactory: () => K, keyDeps: readonly unknown[]) => FetchResult<T, K, I>;
useReload: () => () => void;
}
/**
* Create a [[LedgerContext]]. One should use this function, instead of the default [[DamlLedger]],
* where one needs to be able to nest ledger interactions, by different parties or connections, within
* one React application.
*
* @param contextName Used to refer to a context in case of errors.
*/
export function createLedgerContext(contextName="DamlLedgerContext"): LedgerContext {
// NOTE(MH, useEffect dependencies): There are various places in this file
// where we need to maintain the dependencies of the `useEffect` hook manually
// and there's no tool to help us enfore they are correct. Thus, we need to be
// extra careful in these locations. If we add too many dependencies, we will
// make unnecessary network requests. If we forget adding some dependencies, we
// not make a new network request although they are required to refresh data.
const ledgerContext = React.createContext<DamlLedgerState | undefined>(undefined);
const DamlLedger: React.FC<LedgerProps> = ({token, httpBaseUrl, wsBaseUrl, party, children}) => {
const [reloadToken, setReloadToken] = useState(0);
const ledger = useMemo(() => new Ledger({token, httpBaseUrl, wsBaseUrl}), [token, httpBaseUrl, wsBaseUrl]);
const state: DamlLedgerState = useMemo(() => ({
reloadToken,
triggerReload: (): void => setReloadToken(x => x + 1),
party,
ledger,
}), [party, ledger, reloadToken]);
return React.createElement(ledgerContext.Provider, {value: state}, children);
}
const useDamlState = (): DamlLedgerState => {
const state = useContext(ledgerContext);
if (!state) {
throw Error(`Trying to use ${contextName} before initializing.`);
}
return state;
}
const useParty = (): Party => {
const state = useDamlState();
return state.party;
}
const useLedger = (): Ledger => {
return useDamlState().ledger;
}
function useQuery<T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory?: () => Query<T>, queryDeps?: readonly unknown[]): QueryResult<T, K, I> {
const state = useDamlState();
const [result, setResult] = useState<QueryResult<T, K, I>>({contracts: [], loading: true});
useEffect(() => {
setResult({contracts: [], loading: true});
const query = queryFactory ? queryFactory() : undefined;
const load = async (): Promise<void> => {
const contracts = await state.ledger.query(template, query);
setResult({contracts, loading: false});
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
load();
// NOTE(MH): See note at the top of the file regarding "useEffect dependencies".
}, [state.ledger, state.reloadToken, template, ...(queryDeps ?? [])]);
return result;
}
function useFetchByKey<T extends object, K, I extends string>(template: Template<T, K, I>, keyFactory: () => K, keyDeps: readonly unknown[]): FetchResult<T, K, I> {
const state = useDamlState();
const [result, setResult] = useState<FetchResult<T, K, I>>({contract: null, loading: true});
useEffect(() => {
const key = keyFactory();
setResult({contract: null, loading: true});
const load = async (): Promise<void> => {
const contract = await state.ledger.fetchByKey(template, key);
setResult({contract, loading: false});
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
load();
// NOTE(MH): See note at the top of the file regarding "useEffect dependencies".
}, [state.ledger, state.reloadToken, template, ...(keyDeps ?? [])]);
return result;
}
function useStreamQuery<T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory?: () => Query<T>, queryDeps?: readonly unknown[]): QueryResult<T, K, I> {
const [result, setResult] = useState<QueryResult<T, K, I>>({contracts: [], loading: true});
const state = useDamlState();
useEffect(() => {
setResult({contracts: [], loading: true});
const query = queryFactory ? queryFactory() : undefined;
console.debug(`mount useStreamQuery(${template.templateId}, ...)`, query);
const stream = state.ledger.streamQuery(template, query);
stream.on('live', () => setResult(result => ({...result, loading: false})));
stream.on('change', contracts => setResult(result => ({...result, contracts})));
stream.on('close', closeEvent => {
console.error('useStreamQuery: web socket closed', closeEvent);
setResult(result => ({...result, loading: true}));
});
return (): void => {
console.debug(`unmount useStreamQuery(${template.templateId}, ...)`, query);
stream.close();
};
// NOTE(MH): See note at the top of the file regarding "useEffect dependencies".
}, [state.ledger, template, ...(queryDeps ?? [])]);
return result;
}
function useStreamFetchByKey<T extends object, K, I extends string>(template: Template<T, K, I>, keyFactory: () => K, keyDeps: readonly unknown[]): FetchResult<T, K, I> {
const [result, setResult] = useState<FetchResult<T, K, I>>({contract: null, loading: true});
const state = useDamlState();
useEffect(() => {
setResult({contract: null, loading: true});
const key = keyFactory();
console.debug(`mount useStreamFetchByKey(${template.templateId}, ...)`, key);
const stream = state.ledger.streamFetchByKey(template, key);
stream.on('change', contract => setResult(result => ({...result, contract})));
stream.on('close', closeEvent => {
console.error('useStreamFetchByKey: web socket closed', closeEvent);
setResult(result => ({...result, loading: true}));
});
setResult(result => ({...result, loading: false}));
return (): void => {
console.debug(`unmount useStreamFetchByKey(${template.templateId}, ...)`, key);
stream.close();
};
// NOTE(MH): See note at the top of the file regarding "useEffect dependencies".
}, [state.ledger, template, ...keyDeps]);
return result;
}
const useReload = (): () => void => {
const state = useDamlState();
return (): void => state.triggerReload();
}
return { DamlLedger, useParty, useLedger, useQuery, useFetchByKey, useStreamQuery, useStreamFetchByKey, useReload };
}

View File

@ -0,0 +1,109 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { createLedgerContext, FetchResult, QueryResult, LedgerProps } from "./createLedgerContext";
import { Party, Template } from '@daml/types';
import Ledger, { Query } from '@daml/ledger';
/**
* @internal
*/
const ledgerContext = createLedgerContext();
/**
* Within a `DamlLedger` one can use the hooks provided here.
*
* @param props React props and children for this element.
*/
export function DamlLedger(props: React.PropsWithChildren<LedgerProps>): React.ReactElement|null {
return ledgerContext.DamlLedger(props);
}
/**
* React hook to get the party currently connected to the ledger.
*/
export function useParty(): Party { return ledgerContext.useParty(); }
/**
* React Hook that returns the Ledger instance to interact with the connected DAML ledger.
*/
export function useLedger(): Ledger { return ledgerContext.useLedger(); }
/**
* React Hook for a ``query`` against the ledger.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*
* @param template The contract template to filter for.
* @param queryFactory A function returning a query. If the query is omitted, all visible contracts of the given template are returned.
* @param queryDeps The dependencies of the query (which trigger a reload when changed).
*
* @return The result of the query.
*/
export function useQuery<T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory: () => Query<T>, queryDeps: readonly unknown[]): QueryResult<T, K, I>
export function useQuery<T extends object, K, I extends string>(template: Template<T, K, I>): QueryResult<T, K, I>
export function useQuery<T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory?: () => Query<T>, queryDeps?: readonly unknown[]): QueryResult<T, K, I> {
return ledgerContext.useQuery(template, queryFactory, queryDeps);
}
/**
* React Hook for a lookup by key against the `/v1/fetch` endpoint of the JSON API.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*
* @param template The template of the contracts to fetch.
* @param keyFactory A function returning the contract key of the contracts to fetch.
* @param keyDeps Dependencies of this hook (for which the fetch is reexecuted on change).
*
* @return The fetched contract.
*/
export function useFetchByKey<T extends object, K, I extends string>(template: Template<T, K, I>, keyFactory: () => K, keyDeps: readonly unknown[]): FetchResult<T, K, I> {
return ledgerContext.useFetchByKey(template, keyFactory, keyDeps);
}
/**
* React Hook to query the ledger, the returned result is updated as the ledger state changes.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*
* @param template The template of the contracts to match.
* @param queryFactory A function returning a query. If the query is omitted, all visible contracts of the given template are returned.
* @param queryDeps The dependencies of the query (for which a change triggers an update of the result)
*
* @return The matching contracts.
*/
export function useStreamQuery<T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory: () => Query<T>, queryDeps: readonly unknown[]): QueryResult<T, K, I>
export function useStreamQuery<T extends object, K, I extends string>(template: Template<T, K, I>): QueryResult<T, K, I>
export function useStreamQuery<T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory?: () => Query<T>, queryDeps?: readonly unknown[]): QueryResult<T, K, I> {
return ledgerContext.useStreamQuery(template, queryFactory, queryDeps);
}
/**
* React Hook to query the ledger. Same as useStreamQuery, but query by contract key instead.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*
* @param template The template of the contracts to match.
* @param queryFactory A function returning a contract key.
* @param queryDeps The dependencies of the query (for which a change triggers an update of the result)
*
* @return The matching (unique) contract.
*/
export function useStreamFetchByKey<T extends object, K, I extends string>(template: Template<T, K, I>, keyFactory: () => K, keyDeps: readonly unknown[]): FetchResult<T, K, I> {
return ledgerContext.useStreamFetchByKey(template, keyFactory, keyDeps);
}
/**
* React Hook to reload all active queries.
*/
export function useReload(): () => void {
return ledgerContext.useReload();
}

View File

@ -1,214 +0,0 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { Template, Party } from "@daml/types";
import Ledger, { CreateEvent, Query } from '@daml/ledger';
import { useState, useContext, useEffect } from "react";
import { DamlLedgerState, DamlLedgerContext } from './context'
// NOTE(MH, useEffect dependencies): There are various places in this file
// where we need to maintain the dependencies of the `useEffect` hook manually
// and there's no tool to help us enfore they are correct. Thus, we need to be
// extra careful in these locations. If we add too many dependencies, we will
// make unnecessary network requests. If we forget adding some dependencies, we
// not make a new network request although they are required to refresh data.
/**
* @internal
*/
const useDamlState = (): DamlLedgerState => {
const state = useContext(DamlLedgerContext);
if (!state) {
throw Error("Trying to use DamlLedgerContext before initializing.")
}
return state;
}
/**
* React hook to get the party currently connected to the ledger.
*/
export const useParty = (): Party => {
const state = useDamlState();
return state.party;
}
/**
* React Hook that returns the Ledger instance to interact with the connected DAML ledger.
*/
export const useLedger = (): Ledger => {
return useDamlState().ledger;
}
/**
* The result of a query against the ledger.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*/
export type QueryResult<T extends object, K, I extends string> = {
/** Contracts matching the query. */
contracts: readonly CreateEvent<T, K, I>[];
/** Indicator for whether the query is executing. */
loading: boolean;
}
/**
* React Hook for a ``query`` against the ledger.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*
* @param template The contract template to filter for.
* @param queryFactory A function returning a query. If the query is omitted, all visible contracts of the given template are returned.
* @param queryDeps The dependencies of the query (which trigger a reload when changed).
*
* @return The result of the query.
*/
export function useQuery<T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory: () => Query<T>, queryDeps: readonly unknown[]): QueryResult<T, K, I>
export function useQuery<T extends object, K, I extends string>(template: Template<T, K, I>): QueryResult<T, K, I>
export function useQuery<T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory?: () => Query<T>, queryDeps?: readonly unknown[]): QueryResult<T, K, I> {
const state = useDamlState();
const [result, setResult] = useState<QueryResult<T, K, I>>({contracts: [], loading: true});
useEffect(() => {
setResult({contracts: [], loading: true});
const query = queryFactory ? queryFactory() : undefined;
const load = async (): Promise<void> => {
const contracts = await state.ledger.query(template, query);
setResult({contracts, loading: false});
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
load();
// NOTE(MH): See note at the top of the file regarding "useEffect dependencies".
}, [state.ledger, state.reloadToken, template, ...(queryDeps ?? [])]);
return result;
}
/**
* The result of a ``fetch`` against the ledger.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*/
export type FetchResult<T extends object, K, I extends string> = {
/** Contracts of the given contract template and key. */
contract: CreateEvent<T, K, I> | null;
/** Indicator for whether the fetch is executing. */
loading: boolean;
}
/**
* React Hook for a lookup by key against the `/v1/fetch` endpoint of the JSON API.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*
* @param template The template of the contracts to fetch.
* @param keyFactory A function returning the contract key of the contracts to fetch.
* @param keyDeps Dependencies of this hook (for which the fetch is reexecuted on change).
*
* @return The fetched contract.
*/
export function useFetchByKey<T extends object, K, I extends string>(template: Template<T, K, I>, keyFactory: () => K, keyDeps: readonly unknown[]): FetchResult<T, K, I> {
const state = useDamlState();
const [result, setResult] = useState<FetchResult<T, K, I>>({contract: null, loading: true});
useEffect(() => {
const key = keyFactory();
setResult({contract: null, loading: true});
const load = async (): Promise<void> => {
const contract = await state.ledger.fetchByKey(template, key);
setResult({contract, loading: false});
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
load();
// NOTE(MH): See note at the top of the file regarding "useEffect dependencies".
}, [state.ledger, state.reloadToken, template, ...(keyDeps ?? [])]);
return result;
}
/**
* React Hook to query the ledger, the returned result is updated as the ledger state changes.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*
* @param template The template of the contracts to match.
* @param queryFactory A function returning a query. If the query is omitted, all visible contracts of the given template are returned.
* @param queryDeps The dependencies of the query (for which a change triggers an update of the result)
*
* @return The matching contracts.
*
*/
export function useStreamQuery<T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory: () => Query<T>, queryDeps: readonly unknown[]): QueryResult<T, K, I>
export function useStreamQuery<T extends object, K, I extends string>(template: Template<T, K, I>): QueryResult<T, K, I>
export function useStreamQuery<T extends object, K, I extends string>(template: Template<T, K, I>, queryFactory?: () => Query<T>, queryDeps?: readonly unknown[]): QueryResult<T, K, I> {
const [result, setResult] = useState<QueryResult<T, K, I>>({contracts: [], loading: true});
const state = useDamlState();
useEffect(() => {
setResult({contracts: [], loading: true});
const query = queryFactory ? queryFactory() : undefined;
console.debug(`mount useStreamQuery(${template.templateId}, ...)`, query);
const stream = state.ledger.streamQuery(template, query);
stream.on('live', () => setResult(result => ({...result, loading: false})));
stream.on('change', contracts => setResult(result => ({...result, contracts})));
stream.on('close', closeEvent => {
console.error('useStreamQuery: web socket closed', closeEvent);
setResult(result => ({...result, loading: true}));
});
return (): void => {
console.debug(`unmount useStreamQuery(${template.templateId}, ...)`, query);
stream.close();
};
// NOTE(MH): See note at the top of the file regarding "useEffect dependencies".
}, [state.ledger, template, ...(queryDeps ?? [])]);
return result;
}
/**
* React Hook to query the ledger. Same as useStreamQuery, but query by contract key instead.
*
* @typeparam T The contract template type of the query.
* @typeparam K The contract key type of the query.
* @typeparam I The template id type.
*
* @param template The template of the contracts to match.
* @param queryFactory A function returning a contract key.
* @param queryDeps The dependencies of the query (for which a change triggers an update of the result)
*
* @return The matching (unique) contract.
*/
export function useStreamFetchByKey<T extends object, K, I extends string>(template: Template<T, K, I>, keyFactory: () => K, keyDeps: readonly unknown[]): FetchResult<T, K, I> {
const [result, setResult] = useState<FetchResult<T, K, I>>({contract: null, loading: true});
const state = useDamlState();
useEffect(() => {
setResult({contract: null, loading: true});
const key = keyFactory();
console.debug(`mount useStreamFetchByKey(${template.templateId}, ...)`, key);
const stream = state.ledger.streamFetchByKey(template, key);
stream.on('change', contract => setResult(result => ({...result, contract})));
stream.on('close', closeEvent => {
console.error('useStreamFetchByKey: web socket closed', closeEvent);
setResult(result => ({...result, loading: true}));
});
setResult(result => ({...result, loading: false}));
return (): void => {
console.debug(`unmount useStreamFetchByKey(${template.templateId}, ...)`, key);
stream.close();
};
// NOTE(MH): See note at the top of the file regarding "useEffect dependencies".
}, [state.ledger, template, ...keyDeps]);
return result;
}
/**
* React Hook to reload all active queries.
*/
export const useReload = (): () => void => {
const state = useDamlState();
return (): void => state.triggerReload();
}

View File

@ -2,11 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
// NOTE(MH): Unfortunately the `act` function triggers this warning by looking
// like a promis without being one.
// like a promise without being one.
/* eslint-disable @typescript-eslint/no-floating-promises */
import React, { ComponentType, useState } from 'react';
import { renderHook, RenderHookResult, act } from '@testing-library/react-hooks';
import DamlLedger, { useParty, useQuery, useFetchByKey, useStreamQuery, useStreamFetchByKey, useReload } from './index';
import DamlLedger, { useParty, useQuery, useFetchByKey, useStreamQuery, useStreamFetchByKey, useReload, createLedgerContext } from './index';
import { Template } from '@daml/types';
import { Stream } from '@daml/ledger';
import {EventEmitter} from 'events';
@ -466,5 +466,30 @@ describe('useStreamFetchByKey', () => {
act(() => result.current.setKey(key2));
expect(mockStreamFetchByKey).toHaveBeenCalledTimes(1);
expect(mockStreamFetchByKey).toHaveBeenLastCalledWith(Foo, key2);
});
describe('createLedgerContext', () => {
test('contexts can nest', () => {
const innerLedger = createLedgerContext();
const innerTOKEN = "inner_TOKEN";
const innerPARTY = "inner_PARTY";
const outerLedger = createLedgerContext('Outer');
const outerTOKEN = "outer_TOKEN";
const outerPARTY = "outer_PARTY";
const innerWrapper: ComponentType = ({children}) => React.createElement(innerLedger.DamlLedger, {token:innerTOKEN, party:innerPARTY}, children);
const r1 = renderHook(() => innerLedger.useParty(), {wrapper:innerWrapper});
expect( r1.result.current).toBe(innerPARTY);
const outerWrapper: ComponentType = ({children}) => React.createElement(outerLedger.DamlLedger, {token:outerTOKEN, party:outerPARTY}, innerWrapper({children}) );
const r2 = renderHook(() => outerLedger.useParty(), {wrapper:outerWrapper});
expect( r2.result.current).toBe(outerPARTY);
const r3 = renderHook(() => innerLedger.useParty(), {wrapper:outerWrapper});
expect( r3.result.current).toBe(innerPARTY);
})
})
});

View File

@ -1,8 +1,8 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import DamlLedger from './DamlLedger';
export { createLedgerContext, FetchResult, LedgerContext, QueryResult } from './createLedgerContext';
import { DamlLedger, useParty, useLedger, useQuery, useFetchByKey, useStreamQuery, useStreamFetchByKey, useReload } from "./defaultLedgerContext";
export { useParty, useLedger, useQuery, useFetchByKey, useStreamQuery, useStreamFetchByKey, useReload };
export default DamlLedger;
export * from './hooks';