Skip to Content

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:

NameTypeDescription
dataApplicationData | undefinedShared application data
modalContextunknownRich modal context data
paramsApplicationParams | undefinedAdditional parameters passed to the application
isLoadingbooleanIndicates 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) => voidMethod to navigate to a different route. Pass newTab: true to have the shell host open it in a new browser tab
shellBaseUrlstring | undefinedAbsolute base url of the shell application this app is embedded in
resolveShellUrl(path: string) => stringResolves an application path into an absolute url on the shell
showModalDialog(title: string, content: string) => voidDisplay a modal dialog
showConfirmationDialog(title: string, content: string) => booleanDisplay a confirmation dialog
showToast(content: string, type: 'success' | 'warning' | 'error' | 'loading' | 'destroy') => voidDisplay a toast notification
showErrorMessage(title: string, content: string) => voidDisplay an error message dialog
showRichModal(path: string, context?: unknown) => voidDisplay a rich content modal
removeSearchParams(keys: string | string[]) => voidRemove search params from the URL
setSearchParams(searchParams: Record<string, string | number>) => voidSet 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 }).

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.

shellBaseUrl is undefined until the shell’s first data emit reaches the client — the ordinary state of every app for its first moments — and stays undefined when the host implements no ShellNavigation.resolveUrl, since the shell never guesses one from the page origin. resolveShellUrl then returns the application path, which an anchor resolves against the iframe. Check shellBaseUrl in render, never in a memo that outlives the first emit, and fall back to navigate({ 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.