Hooks
The @akinon/app-client library provides a set of React hooks that allow you to
easily access and utilize the functionality provided by the app client context.
useAppClient
The useAppClient hook is the primary way to access the app client context and
its associated functions within your components.
Usage
import { useAppClient } from '@akinon/app-client';
const MyComponent = () => {
const {
data,
params,
isLoading,
invokeAction,
navigate,
resolveShellUrl,
showModalDialog,
showConfirmationDialog,
showToast,
showErrorMessage,
showRichModal,
removeSearchParams,
setSearchParams
} = useAppClient();
// Use the context values and functions in your component
};Return Value
The useAppClient hook returns an object with the following properties and
methods:
| Name | Type | Description |
|---|---|---|
data | ApplicationData | undefined | Shared application data |
modalContext | unknown | Rich modal context data |
params | ApplicationParams | undefined | Additional parameters passed to the application |
isLoading | boolean | Indicates whether the application data is currently loading |
invokeAction | <T = any>(actionKey: string, ...args: any[]) => Promise<T> | Method to invoke a custom action defined in the app shell |
navigate | (payload: ShellNavigationPayload) => void | Method to navigate to a different route. Pass newTab: true to have the shell host open it in a new browser tab |
shellBaseUrl | string | undefined | Absolute base url of the shell application this app is embedded in |
resolveShellUrl | (path: string) => string | Resolves an application path into an absolute url on the shell |
showModalDialog | (title: string, content: string) => void | Display a modal dialog |
showConfirmationDialog | (title: string, content: string) => boolean | Display a confirmation dialog |
showToast | (content: string, type: 'success' | 'warning' | 'error' | 'loading' | 'destroy') => void | Display a toast notification |
showErrorMessage | (title: string, content: string) => void | Display an error message dialog |
showRichModal | (path: string, context?: unknown) => void | Display a rich content modal |
removeSearchParams | (keys: string | string[]) => void | Remove search params from the URL |
setSearchParams | (searchParams: Record<string, string | number>) => void | Set search params in the URL |
Opening a call to action in a new browser tab
A path resolved inside the iframe is a path on the iframe’s own origin, which is
not where the user is. Two members of the context exist for this: navigate
takes a newTab flag and lets the shell host open the window, and
resolveShellUrl turns an application path into an absolute url on the shell,
so you can render a real anchor — one the user can middle-click, copy, or hover
to see where it goes.
import React from 'react';
import { useAppClient } from '@akinon/app-client';
const OrderCallToAction = ({ orderId }) => {
const { navigate, resolveShellUrl, shellBaseUrl } = useAppClient();
const path = `/orders/${orderId}`;
if (shellBaseUrl) {
return (
<a href={resolveShellUrl(path)} rel="noopener noreferrer" target="_blank">
Open order in a new tab
</a>
);
}
return (
<button onClick={() => navigate({ path, newTab: true })}>
Open order in a new tab
</button>
);
};Both land on the same url. resolveShellUrl resolves application paths only,
and prefixes the application’s own base path exactly as navigate does; a shell
destination outside your application is reachable through
navigate({ path, external: true, newTab: true }).
Deep links carry no query string
Give resolveShellUrl a path with a query string and it drops everything from
the first ?. This is deliberate: the shell host drops the query string on
internal navigation as well, so an anchor that kept it would send the user
somewhere the button beside it never goes — and would carry into a new tab
exactly the data the programmatic path withholds. A hash is kept, because the
host keeps it. Pass state that has to survive the jump in the path itself, or
wait for
the ADR that makes deep-link query strings a protocol feature ;
both navigation paths will start honouring them together.
A new tab needs the user’s gesture
Call navigate({ newTab: true }) synchronously from the click handler. The
shell opens the window inside a postMessage handler, and a browser allows that
only while the gesture is still live; after an await, the popup blocker takes
over and the navigation is lost with no error to catch.
shellBaseUrlisundefineduntil the shell’s first data emit reaches the client — the ordinary state of every app for its first moments — and staysundefinedwhen the host implements noShellNavigation.resolveUrl, since the shell never guesses one from the page origin.resolveShellUrlthen returns the application path, which an anchor resolves against the iframe. CheckshellBaseUrlin render, never in a memo that outlives the first emit, and fall back tonavigate({ path, newTab: true }).
Example
import React from 'react';
import { useAppClient } from '@akinon/app-client';
const NavigationButton = () => {
const { navigate } = useAppClient();
return (
<button onClick={() => navigate({ path: '/dashboard' })}>
Go to Dashboard
</button>
);
};
const ActionButton = () => {
const { invokeAction } = useAppClient();
const handleClick = async () => {
try {
const result = await invokeAction('customAction', 'arg1', 'arg2');
console.log('Action result:', result);
} catch (error) {
console.error('Action failed:', error);
}
};
return <button onClick={handleClick}>Invoke Custom Action</button>;
};
const SearchParamsExample = () => {
const { setSearchParams, removeSearchParams } = useAppClient();
return (
<div>
<button onClick={() => setSearchParams({ filter: 'active', page: 1 })}>
Set Filters
</button>
<button onClick={() => removeSearchParams(['filter', 'page'])}>
Clear Filters
</button>
</div>
);
};By using the useAppClient hook, you can easily access and utilize the various
functionalities provided by the app client context throughout your application.