C3 AI Documentation Home

Dispatch actions

The UI Framework uses Redux to manage application state in a centralized way. This architecture allows components to communicate by dispatching and subscribing to Redux actions. For example:

  • Dispatch an action using useDispatch when a UI event occurs (like a button click).
  • The Redux store updates global state in response.
  • Subscribed components receive the new state or side effects and respond accordingly.

Use this pattern to synchronize behavior between decoupled UI elements or to trigger backend calls.

Redux actions

A Redux action is a plain JavaScript object representing an event:

JSON
{
  type: "ComponentId.EVENT_NAME",
  payload: {
    // Structured data for reducers, middleware, or subscribed components
  }
}

Each action includes a type field to identify the event and a payload field to provide supporting data.

Dispatch actions from a TSX component

Use useDispatch to send Redux actions from custom components. This example shows a TSX-based button dispatching an action that is later consumed by another component.

Trigger Redux Actions from a TSX Component

This example defines a button as a React component. The button uses the useDispatch hook to send a Redux action when clicked. The action signals to the Redux store that a component initiated an event and includes a payload with context for consumers.

The application uses a scoped identifier (examplePackage.MyButton) to namespace the action type. This pattern ensures that actions route correctly through the Redux store and that other components can subscribe to them with confidence.

The action includes a type field to describe the event (CUSTOM_BUTTON_CLICKED) and a payload object to carry data. In this case, the payload identifies the button and describes the intended display behavior. Components that subscribe to this action can react immediately — for example, by showing a table, loading new data, or updating the layout.

The UI Framework handles this flow by wiring the dispatch() call directly into the Redux system. As a result, components do not need to manage global state manually. The store tracks application events, coordinates state updates, and notifies interested components.

TypeScript
// {package}/ui/c3/src/customInstances/examplePackage.TSXButton.tsx
import { useDispatch } from '@c3/ui/UiSdlUseDispatch';
import * as React from 'react';

const TSXButton = () => {
  // Unique namespace for application state and action types
  const COMPONENT_ID = 'examplePackage.MyButton';

  // Hook into the Redux store’s dispatch method
  const dispatch = useDispatch();

  // Click handler for the button
  const clickHandler = () => {
    const globalAction = {
      type: `${COMPONENT_ID}.CUSTOM_BUTTON_CLICKED`, // Action name
      payload: {
        componentId: 'examplePackage.TSXButton',
        value: 'display' // Data passed to listeners
      }
    };

    dispatch(globalAction); // Send action to Redux store
  };

  // Render the dispatching button
  return <button onClick={clickHandler}>Use Fetch</button>;
};

export default TSXButton;

Dispatch actions from a JSON-defined button

In declarative UI configurations, you can define buttons using JSON. These buttons can trigger Redux actions via effectTriggers.

Define a JSON Button That Triggers a Redux Action

This example defines a button declaratively using the JSON. The button uses the effectTriggers field to describe how it should respond to events. When a user clicks the button, it emits the examplePackage.JSONButton.BUTTON_CLICK event.

The UI Framework responds by dispatching a Redux action. This action uses a type string of examplePackage.MyButton.BUTTON_CLICK and includes a payload with a value field set to "display". This structure matches what subscribed components expect and allows them to react in a consistent way.

The JSON defines both the event trigger and the resulting action in one place. This format eliminates the need to write TypeScript for common UI interactions. Developers can wire behavior declaratively and rely on the framework to route events through the Redux store.

The Redux system delivers the action to all registered reducers, epics, and listeners. Subscribed components receive the payload and respond by updating local state or the rendered output.

This configuration emits an action to the Redux store whenever the button is clicked—no TypeScript required.

JSON
// {package}/ui/c3/meta/examplePackage.JSONButton.json
{
  "type": "UiSdlConnected<UiSdlButton>",
  "component": {
    "content": "JSON Button",
    "disabled": false
  },
  "effectTriggers": [{
    "trigger": "examplePackage.JSONButton.BUTTON_CLICK", // Event emitted on click
    "actions": [
      {
        "type": "examplePackage.MyButton.BUTTON_CLICK", // Dispatched action type
        "payload": {
          "value": "display" // Optional data sent with the action
        }
      }
    ]
  }]
}

WindTurbineTable: Reacts to actions and renders data

This table component listens for the actions dispatched by either the TSX or JSON buttons and displays data in response.

Registers a Redux action listener

This component loads wind turbine data and listens for a Redux action to control visibility. The component uses the useC3Action hook to fetch data from the backend. The request targets the getWindTurbineByStatus method and filters for turbines with an active status.

After the data loads, the component registers a Redux action listener using addReduxActionListener. The listener watches for the examplePackage.MyButton.BUTTON_CLICK action, which can come from either a JSON-defined or TSX-defined button.

When the listener detects the action, it reveals the turbine table by changing the element's display style. This interaction connects remote button events to a visual change in the component without direct wiring or shared props.

The Redux store handles the action delivery. The component stays focused on rendering and behavior while the store coordinates communication. This pattern supports clean boundaries between components and consistent state-driven control.

TypeScript
// {package}/ui/c3/src/customInstances/examplePackage.TurbineTable.tsx
import React from 'react';
// Hook to call backend action defined in the C3 model
import { useC3Action } from '@c3/ui/UiSdlUseC3Action';
// Utility to register custom Redux action listeners
import { addReduxActionListener } from '@c3/app/ui/src/epicRegistry';

const WindTurbineTable = () => {
  // Holds rows of turbine data in local state
  const [rows, setRows] = React.useState([]);

  // Backend API call spec: fetch turbines with status = 1
  const actionSpec = {
    typeName: 'WindTurbine', // Entity name
    actionName: 'getWindTurbineByStatus', // Backend method
    argsArray: { status: 1 },
    method: 'POST'
  };

  // Executes the backend call and returns data & status
  const response = useC3Action(actionSpec);

  // Once data loads, set up a Redux action listener
  React.useEffect(() => {
    if (response.status === 'done') {
      setRows(response.data); // Store data locally

      // Listen for the dispatched BUTTON_CLICK action
      // Can be triggered from either JSONButton or TSXButton
      addReduxActionListener(`examplePackage.MyButton.BUTTON_CLICK`, (action, state) => {
        console.log('Received action:', action);

        // Reveal the table when the action is detected
        (document.querySelector('.wind-turbine-table') as HTMLElement).style.display = 'block';
      });
    }
  }, [response]);

  // Render a hidden table that shows turbine data after activation
  return (
    <div className="wind-turbine-table" style={{ display: 'none' }}>
      <h1>Wind Turbines</h1>
      <table>
        <thead>
          <tr>
            <th>Serial</th>
            <th>Manufacturer</th>
            <th>Location</th>
            <th>Installed</th>
            <th>Status</th>
          </tr>
        </thead>
        <tbody>
          {rows.map((row) => (
            <tr id={row.id} key={row.id}>
              <td>{row.serialNumber}</td>
              <td>{row.manufacturer}</td>
              <td>{row.location}</td>
              <td>{new Date(row.installationDate).toLocaleDateString()}</td>
              <td>{row.status === 1 ? 'RUNNING' : 'OFFLINE'}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};

export default WindTurbineTable;

This component reacts to Redux actions whether they originate from TSX or declarative JSON, making it flexible for cross-component communication.

Validate your action

To confirm correct dispatch behavior:

  1. Install Redux DevTools.

  2. Build and launch your application.

  3. Open the browser page with the TSX or JSON button.

  4. Click the button.

  5. Check the DevTools for the expected action type:

    • examplePackage.MyButton.CUSTOM_BUTTON_CLICKED (TSX)
    • examplePackage.MyButton.BUTTON_CLICK (JSON)

If nothing appears, check that the component is wrapped in a Redux provider and that the action type matches your listener.

Was this page helpful?