React Hooks in the Component SDK
Author:
Fluent Commerce
Changed on:
15 July 2024
Overview
Component SDK provides a collection of React hooks to allow custom components to access the core UX framework functionality.
The SDK includes Typescript declaration files that tell your IDE and compiler what these look like and that they are available on the global scope.
Key points
- Component SDK provides a collection of React hooks to allow custom components to access the core UX framework functionality.
- The useAuth can access user session information.
- The useEnv can access the current environment details.
- The useI18n can access the global language to translate static labels
- The useSetting / getSettings can access the current account setting values.
- The useQuery/getQuery and useRest/getRest to retrieve data from OMS.
- The useData hook provides direct access to the page query response and variables.
Hooks
Component SDK provides a collection of React hooks to allow custom components to access the core UX framework functionality.
The SDK includes Typescript declaration files that tell your IDE and compiler what these look like and that they are available on the global scope.
useAuth
User login session information, provides access to:
- details of the logged in user
- current roles and permissions
- ability to switch contexts (i.e. between different retailers or locations the user has access to)
- ability to log out
1// SampleUseAuth.tsx
2
3import { FC } from 'react';
4import { useAuth } from 'mystique/hooks/useAuth';
5
6export const SampleUseAuth: FC = () => {
7 const auth = useAuth();
8
9 const SimpleLayout = (heading: string, content: string) => {
10 return (
11 <div>
12 <h3>
13 <u>{heading}:</u>
14 </h3>
15 {content}
16 <br /> <br />
17 </div>
18 );
19 };
20
21 return (
22 <div>
23 <h1>useAuth Example</h1>
24 <h2>Examples of reteiving data from useAuth</h2>
25 {SimpleLayout('auth json:', JSON.stringify(auth))}
26
27 {SimpleLayout(
28 'JSON.stringify(auth.context)',
29 JSON.stringify(auth.context)
30 )}
31
32 {SimpleLayout(
33 'retailer Ref auth.context.current.details?.ref',
34 auth.context.current.details?.ref
35 )}
36
37 {SimpleLayout(
38 'username auth.context.current.details?.ref',
39 auth.user.username
40 )}
41 </div>
42 );
43};
44
45//add the following into index.tsx:
46import { SampleUseAuth } from './components/SampleUseAuth';
47
48ComponentRegistry.register(['SampleUseAuth'], SampleUseAuth, {
49 category: 'content',
50});
51
useEnv
Details of the current environment:
- the Fluent Account name
- the current Web App name (eg. "oms")
1// SampleUseAuth.tsx
2
3import { useEnv } from 'mystique/hooks/useEnv';
4import { FC } from 'react';
5
6export const SampleUseEnv: FC = () => {
7 const env = useEnv();
8
9 const SimpleLayout = (heading: string, content: string) => {
10 return (
11 <div>
12 <h3>
13 <u>{heading}:</u>
14 </h3>
15 {content}
16 <br /> <br />
17 </div>
18 );
19 };
20
21 return (
22 <div>
23 <h1>useAuth Example</h1>
24 <h2>Examples of reteiving data from useAuth</h2>
25 {SimpleLayout('JSON.stringify(env)', JSON.stringify(env))}
26
27 {SimpleLayout('env.app', env.app)}
28 {SimpleLayout('env.account', env.account)}
29 {SimpleLayout('env.api', env.api)}
30 </div>
31 );
32};
33
34
35//add the following into index.tsx:
36import { SampleUseEnv } from './components/SampleUseEnv';
37
38ComponentRegistry.register(['SampleUseEnv'], SampleUseEnv, {
39 category: 'content',
40});
41
useI18n
Automatically translate static labels, generally for things that aren't coming from configuration (as those are automatically translated).
If the user changes languages during a session, these strings will automatically be re-translated and the component will re-render without requiring additional code.
For example, on an ID confirmation component:
1import { Card } from 'mystique/components/Card';
2import { useI18n } from 'mystique/hooks/useI18n';
3import { FC } from 'react';
4
5const ConfirmID: FC = () => {
6 const { translate } = useI18n();
7 return (
8 <Card>
9 <h2>{translate('acme.collection.customerConfirmMessage')}</h2>
10 <Button label={translate('acme.collection.customerConfirmButton')} />
11 </Card>
12 );
13}
In some cases you might find yourself with several keys that might fit the label, or want to provide a default value in the code. The `translateOr`
function allows several keys to be passed in, and will return either the value of the first language-file match, or the literal value of the last key (as a default).
1const ErrorDialogue: FC = () => {
2 const {translateOr} = useI18n();
3 return (
4 <span>{translateOr([
5 'acme.errors.specificErrorMessage',
6 'acme.errors.genericErrorMessage',
7 'There was a problem' // will be returned if no key above matches
8 ])}</span>
9 );
10}
11
Finally, the useI18n hook provides information about the available and currently selected languages.
useSettings
Many components are configured at a retailer or level using settings. The useSetting hook gives easy access to those values.
By default, `useSettings`
will find the setting value at the current context the user is logged into. Depending on the web app setup this could be a or a retailer.
From there, it will cascade up the context hierarchy (, to retailer, to ) until it finds a setting of the name provided and return the first (meaning most specific) match.
1const SettingsBasedSelect: FC<SettingBasedSelectProps> = ({setting}) => {
2 const conf = useSettings({'options': setting});
3
4 if(conf.options.status === 'loading') {
5 return Spinner;
6 }
7 return (
8 <select>
9 {conf.options.setting.value.map((opt) => (<option value={opt.value}>{opt.label}</option>))}
10 </select>
11 );
12}
13
14
15
16// alternate way to use useSettings:
17import { useSettings } from 'mystique/hooks/useSettings';
18import { FC } from 'react';
19
20export const SampleFieldUseSettings: FC = () => {
21 const settings = useSettings({ key: 'i18n.languages' });
22
23 if (settings.key.status === 'loading') {
24 return <div>loading</div>;
25 }
26
27 return (
28 <div>
29 <h1>SampleFieldUseSettings</h1>
30 <h4>JSON.stringify(settings.key.result?.value):</h4>
31 <div>{JSON.stringify(settings.key.result?.value)}</div>
32 <h4>settings.key.result?.value.accountDefaultLanguage</h4>
33 <div>{settings.key.result?.value.accountDefaultLanguage}</div>
34 </div>
35 );
36};
37
38
39
40
41
getSettings
For cases where a hook is not a good fit, a Promise-based variant of `useSettings`
is provided in `mystique/hooks/getSettings`
.
An example can be found in here
useQuery and useRest
For most cases we strongly encouraged letting the UX framework build and run the query based on the configuration, for performance and complexity reasons, but there are cases in which a single query is not enough to gather all of the data required for a given page. This is especially true for cases where the result of one query is needed to build the follow-up query.
The useQuery is available to components for those edge cases.
The useRest example can be found here.
A simple useQuery Example
1import { useQuery } from 'mystique/hooks/useQuery';
2import { Connection } from 'mystique/types/common';
3import { FC } from 'react';
4
5interface NetworkNode {
6 id: string;
7 ref: string;
8 status: string;
9}
10
11interface NetworkResult {
12 NetworkResult: Connection<NetworkNode>;
13}
14
15const networkQuery = `
16query getNetworks{
17 networks(first:5){
18 edges{
19 node{
20 id
21 ref
22 status
23 type
24 }
25 }
26 }
27}`;
28
29export const SampleFieldHooks2: FC = () => {
30 const [res] = useQuery<NetworkResult>(networkQuery);
31
32 return (
33 <div>
34 <h1>SampleFieldHooks2</h1>
35 <h4>Example of useQuery without any query input vars</h4>
36 <h4>JSON.stringify(res.data)</h4>
37 <div>{JSON.stringify(res.data)}</div>
38 </div>
39 );
40};
41
useQuery Example with useAuth() as query variable
1import { useQuery } from 'mystique/hooks/useQuery';
2import { Connection } from 'mystique/types/common';
3import { useAuth } from 'mystique/hooks/useAuth';
4import { FC } from 'react';
5
6interface OrderNode {
7 id: string;
8 ref: string;
9 status: string;
10 type: string;
11 retailerId: { id: string };
12}
13
14interface OrderResult {
15 OrderResult: Connection<OrderNode>;
16}
17
18const orderQuery = `
19query getOrders($retailerId: [Int!]){
20 orders(retailerId:$retailerId){
21 edges{
22 node{
23 id
24 ref
25 status
26 type
27 retailer{ id }
28 }
29 }
30 }
31}`;
32
33export const SampleFieldHooks3: FC = () => {
34 const auth = useAuth();
35 const [res] = useQuery<OrderResult>(orderQuery, {
36 retailerId: auth.context.current.contextId,
37 });
38
39 return (
40 <div>
41 <h1>SampleFieldHooks3 - auth as variable</h1>
42 <h4>Example of useQuery with useAuth() as a query variable</h4>
43 <h4>JSON.stringify(res.data)</h4>
44 <div>{JSON.stringify(res.data)}</div>
45 </div>
46 );
47};
48
Example of useQuery to handle 'edges' nodes in the result.
1import { useQuery } from 'mystique/hooks/useQuery';
2import { Connection } from 'mystique/types/common';
3import { useAuth } from 'mystique/hooks/useAuth';
4import { FC } from 'react';
5
6interface OrderItemNode {
7 id: string;
8 ref: string;
9 quantity: string;
10}
11
12interface OrderNode {
13 id: string;
14 ref: string;
15 status: string;
16 type: string;
17 retailerId: { id: string };
18 OrderItems: Array<OrderItemNode>;
19 // alternative way to declare OrderItem:
20 // OrderItems: Array<{ id: string; ref: string; quantity: string }>;
21}
22
23interface OrderResult {
24 OrderResult: Connection<OrderNode>;
25}
26
27const orderQuery = `
28query getOrders($retailerId: [Int!]){
29 orders(retailerId:$retailerId){
30 edges{
31 node{
32 id
33 ref
34 status
35 type
36 retailer{ id }
37 items{
38 edges{
39 node{
40 id
41 ref
42 quantity
43 }
44 }
45 }
46 }
47 }
48 }
49}`;
50
51export const SampleFieldHooks4: FC = () => {
52 const auth = useAuth();
53 const [res] = useQuery<OrderResult>(orderQuery, {
54 retailerId: auth.context.current.contextId,
55 });
56
57 return (
58 <div>
59 <h1>SampleFieldHooks4 - auth as variable with edges within the result</h1>
60 <h4>
61 Example of useQuery with Auth as the query variable. edges in the
62 result.
63 </h4>
64 <h4>JSON.stringify(res.data)</h4>
65 <div>{JSON.stringify(res.data)}</div>
66 </div>
67 );
68};
69
Example of useQuery using contextEntity as input variable
1import { useQuery } from 'mystique/hooks/useQuery';
2import { Connection } from 'mystique/types/common';
3import { FC } from 'react';
4
5interface LocationNode {
6 id: string;
7 ref: string;
8 status: string;
9}
10
11interface LocationResult {
12 locations: Connection<LocationNode>;
13}
14
15const locationQuery = `
16query getLocations($locationRef:[String]){
17 locations(ref:$locationRef){
18 edges{
19 node{
20 id
21 ref
22 status
23 }
24 }
25 }
26}`;
27
28
29export const SampleFieldHooks5: FC = ({ entityContext }) => {
30 const [res] = useQuery<LocationResult>(locationQuery, {
31 locationRef: entityContext?.[0].entity.ref,
32 });
33
34 return (
35 <div>
36 <h1>SampleFieldHooks5: useQuery using entityContext as input vars</h1>
37 <h4>Example of useQuery using contextEntity as input vars</h4>
38 <h4>{JSON.stringify(res.data)}</h4>
39 <div>{JSON.stringify(entityContext)}</div>
40 </div>
41 );
42};
43
Example of useQuery and use Map function to display the data
1import { useQuery } from 'mystique/hooks/useQuery';
2import { Connection } from 'mystique/types/common';
3import { useAuth } from 'mystique/hooks/useAuth';
4import { FC } from 'react';
5
6interface OrderItemNode {
7 id: string;
8 ref: string;
9 quantity: string;
10}
11
12interface OrderNode {
13 id: string;
14 ref: string;
15 status: string;
16 type: string;
17 retailerId: { id: string };
18 OrderItems: Array<OrderItemNode>;
19 // alternative way to declare OrderItem:
20 // OrderItems: Array<{ id: string; ref: string; quantity: string }>;
21}
22
23interface OrderResult {
24 orders: Connection<OrderNode>;
25}
26
27const orderQuery = `
28query getOrders($retailerId: [Int!]){
29 orders(retailerId:$retailerId){
30 edges{
31 node{
32 id
33 ref
34 status
35 type
36 retailer{ id }
37 items{
38 edges{
39 node{
40 id
41 ref
42 quantity
43 }
44 }
45 }
46 }
47 }
48 }
49}`;
50
51export const SampleFieldUseQueryMap: FC = () => {
52 const auth = useAuth();
53 const [res] = useQuery<OrderResult>(orderQuery, {
54 retailerId: auth.context.current.contextId,
55 });
56
57 return (
58 <div>
59 <h1>SampleFieldUseQueryMap</h1>
60 <h4>Example of useQuery</h4>
61 <h4>JSON.stringify(res.data)</h4>
62 <div>{JSON.stringify(res.data)}</div>
63 <h4>res.data?.orders.edges.map((row) => </h4>
64 <table border='1'>
65 <tr>
66 <th>orderId</th>
67 <th>orderRef</th>
68 </tr>
69 {res.data?.orders.edges.map((row) => (
70 <tr>
71 <td>{row.node.id}</td>
72 <td>{row.node.ref}</td>
73 </tr>
74 ))}
75 </table>
76 </div>
77 );
78};
79
Example of useQuery returning a single result
1import { useQuery } from 'mystique/hooks/useQuery';
2import { FC } from 'react';
3
4//
5// Example of useQuery returning 1 entity
6//
7
8interface LocationNode {
9 id: string;
10 ref: string;
11 status: string;
12}
13
14interface LocationResult {
15 // The var name must be same as the query.
16 locationById: LocationNode;
17 // Since the query will return 1 entity, you do not need to use
18 // Connection<>. but it still returning result...
19 //locationById: Connection<LocationNode>;
20}
21
22const locationQuery = `
23query getLocationById($locationId:ID!){
24 locationById(id:$locationId){
25 id
26 ref
27 status
28 }
29}`;
30
31export const SampleFieldUseQuerySingleResult: FC = ({ entityContext }) => {
32 const [res] = useQuery<LocationResult>(locationQuery, {
33 locationId: entityContext?.[0].entity.id,
34 });
35 return (
36 <div>
37 <h1>SampleFieldUseQuerySingleResult: returning 1 entity in Result</h1>
38 <h4>
39 returning 1 entity with <b>any</b> Type
40 </h4>
41 <h4>JSON.stringify(res):</h4>
42 <div>{JSON.stringify(res)}</div>
43 <h4>JSON.stringify(res.data):</h4>
44 <div>{JSON.stringify(res.data)}</div>
45 <h4>res.data?.locationById.ref:</h4>
46 <div>{res.data?.locationById.ref}</div>
47 <h4>res.data?.locationById.id:</h4>
48 <div>{res.data?.locationById.id}</div>
49 <h4>res.data?.locationById.status:</h4>
50 <div>{res.data?.locationById.status}</div>
51 </div>
52 );
53};
54
getQuery and getRest
For cases where a hook is not a good fit, a Promise-based variant of each is provided in `mystique/hooks/getQuery`
and `mystique/hooks/getRest`
respectively.
An example of `getQuery`
can be found here
getApiDownload
There are a few Fluent REST API endpoints that produce a file.
The getApiDownload hook (module `mystique/hooks/getApiDownload`
) allows a component to retrieve this data in the form of a blob, which can then either be rendered or downloaded as required.
useData
The useData hook provides direct access to the page query response and variables.
It can be used to build components that alter the page query in response to user interaction, like the list filter.
1import { useData } from 'mystique/hooks/useData';
2import { FC } from 'react';
3
4export const SampleFieldUseData: FC = () => {
5 const data = useData();
6
7 return (
8 <div>
9 <h1>SampleFieldUseData</h1>
10 <h4>JSON.stringify(data):</h4>
11 <div>{JSON.stringify(data)}</div>
12 <h4></h4>
13 <div></div>
14 </div>
15 );
16};
17
useUserActions and useUserActionForm
User actions are used to indicate in the that a should appear in the UI. This usually take the form of a button which, if required, presents a modal to capture any extra information needed to the .
The useUserActions hook allows an SDK developer to get a list of the available user actions on any returned by the page query.
By default, it will return user actions for the first found at the current dataSource root (i.e. the `data`
object passed into this component), but this can be changed using the `path`
parameter (in the form of a `JSONPath`
relative to the current dataSource root).
The useUserActionForm generates a form for a named . The target is chosen using the same logic as the useUserActions hook.