Calling an action

If you want to trigger an operation on a backend system or want to fetch data asynchronously after initial rendering, you call an action extension (see the developing an action extension article for more information).

For this example, we'll use the following Frontend component to implement searching a backend data set using an action:

import React from 'react';
const CharacterSearchTastic: React.FC = () => {
return (
<>
<form
onSubmit={(event) => {
event.preventDefault();
}}
>
<label>
Search:
<input type="text" name="search" />
</label>
<input type="submit" value="Search" />
</form>
<ul></ul>
</>
);
};
export default CharacterSearchTastic;
{
"tasticType": "example/character-search",
"name": "Character search",
"category": "Example",
"description": "A frontend component showing actions and session handling",
"schema": []
}

The Frontend component doesn't provide any configuration to studio and (so far) only renders a rudimentary search form and an empty list.

Perform the fetch call

An action extension is a server function in the API hub which can be invoked by a URL in the following format: https://<frontastic-host>/frontastic/action/<namespace>/<action>(also see the action API section).

To access it, you should always use the fetchApiHub() function:

import { fetchApiHub } from 'frontastic';
export const characterSearch = async (search: string) => {
return await fetchApiHub(
'/action/star-wars/character?search=' + encodeURIComponent(search)
);
};

The fetchApiHub() function not only resolves the correct API hub host for you but also ensures to maintain the session. For this reason, you should always use the fetchApiHub() function for communication with the API hub.

By convention, you should store the file to encapsulate the fetchApiHub() call in packages/<project>/frontend/frontastic/actions/.

API hub currently doesn't support sending custom headers. You should pass additional information inside the POST request payload instead of using custom headers.

Register fetcher as a hook

The fetch function is wrapped into a higher-order component to receive the base URL to query. This way, it can be used together with the contenxt provider component, where you should register your fetchers in the manner of ReactJS hooks:

// …
import { characterSearch } from '../actions/doc-character-search';
// …
export const FrontasticProvider: React.FC> = ({ children }) => {
return (
<SWRConfig value={{ fetcher: fetchApiHub }}>
<FrontasticContext.Provider
value={{
// ...
useStarWars: {
characterSearch: characterSearch,
},
}}
>
{children}
</FrontasticContext.Provider>
</SWRConfig>
);
};
// …
export const useStarWars = () => {
const context = React.useContext(FrontasticContext);
if (!context) throw new Error('Expected to be wrapped in FrontasticProvider');
return context.useStarWars;
};

The code above needs to be added to the file packages/<project>/frontend/frontastic/lib/provider.tsx.

There are 2 steps:

  1. Register the characterSearch() function on the context provider
  2. If not done yet, expose a fetcher function to make use of the functions in the corresponding hook

In this example, a new hook (useStarWars) is exposed for which a new provider property is used.

The way fetchers are registered in the lib/ folder will change soon.

Execute the hook to fetch search results

Based on this infrastructure code, you can now complete the Frontend component and use the useState() hook to maintain the search results locally:

import React, { useState } from 'react';
import { useStarWars } from 'frontastic';
const CharacterSearchTastic: React.FC = () => {
const { characterSearch } = useStarWars();
const [searchResults, setSearchResults] = useState([]);
return (
<>
<form
onSubmit={(event) => {
event.preventDefault();
characterSearch(event.target[0].value).then((result) => {
setSearchResults(result.results || []);
});
}}
>
<label>
Search:
<input type="text" name="search" />
</label>
<input type="submit" value="Search" />
</form>
<ul>
{searchResults.map((character) => {
return <li key={character.url}>{character.name}</li>;
})}
</ul>
</>
);
};
export default CharacterSearchTastic;