Calling an action

👍

Prerequisites

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 Frontastic 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 Frontastic component doesn't provide any configuration to Frontastic 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 Frontastic 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 Frontastic 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/.

🚧

Sending custom headers

The 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 the Frontastic host to query. This way, it can be used together with the Frontastic 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 Frontastic 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 Frontastic 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;

Did this page help you?