Manage Application State
Learn about Application State, Epics, Reducers, and Actions.
Application state allows for components to be interactive, talk to each other, and more. To manage your application state in UI Framework is similar to Redux state management.

Redux Refresher

Redux uses actions and reducers to modify a global store. This allows us to do state management.
  • Store: Holds the state of the application.
  • Actions: Describes the change. They do not change the state.
  • Reducers: Are functions that take an action and directly change the state.
For more on Redux architecture, see Concepts and Data Flow.

Manage state in UI Framework

The UI Framework uses actions, reducers, and epics to manage the state. The goal of this section is to understand how the UI Framework manages the state in most scenarios.

Trigger epic with component's action

Components often use and manipulate the state. They come with pre-defined set of actions that you can use to trigger your epic from. You can also create custom actions to trigger your epic from. Epics allow for customizations like the filter button on a filter panel or a deletion with a modal.
This is an example of how an action called PRIMARY_BUTTON_CLICK triggers an epic called UiSdlEpicDeleteAfterExample.
1
"effectTriggers": [
2
{
3
"trigger":"exampleUI.customHeaderModal.PRIMARY_BUTTON_CLICK",
4
"effectType": "UiSdlEpicDeleteAfterExample",
5
"payload": {
6
"componentId": "exampleUI.UiSdlApplicationStateEUI",
7
"tableToRefresh": "exampleUI.customTableTypesBasic"
8
},
9
"payloadStrategy":"MERGE"
10
}
11
]
Copied!
Key
Description
effectTriggers
One use of effectTriggers is to add an epic to a component.
trigger
The action that triggers the epic. Syntax is <currentFilename>.<action>.
effectType
The filename of the desired epic that is triggered from the action.

State, actions, and reducers

The UiSdlApplicationState is a type that contains a collection of actions and reducers. To create your own custom action and reducers, you create your own UiSdlApplicationState Type and mixes with UiSdlApplicationState.

Application state

This is an example of a UI Framework directory with the focus on where application state files live in. The application state is a collection of actions and reducers.
1
exampleRepository
2
├── exampleUI
3
| └── src
4
| | └── ui
5
| | | └── UiSdlApplicationStateLBE.c3typ
6
| | | └── UiSdlApplicationStateLBE.ts
7
| └── ...
8
│   └── package.json
9
└── repository.json
Copied!

Actions and reducers

It is best practice for the collection of actions and reducers to live under scr>ui, similar to the example shown. You must create two files with the same name:
  • myApplicationStateCollections.c3typ: declare the actions and reducers.
  • myApplicationStateCollections.ts: write the actions and reducers.
Inside myApplicationStateCollection.c3typ, you must declare your Application State as a Type and must mixes with UiSdlApplicationState.
It is best practice to declare your action before your reducer.
myApplicationStateCollections.c3typ
myApplicationStateCollections.ts
1
@typeScript
2
type myApplicationStateCollections mixes UiSdlApplicationState {
3
/*
4
* Listens for actions of type 'ROW_RECORD_STORE'
5
*/
6
@uiSdlActionCreator(actionType='ROW_RECORD_STORE')
7
@typeScript(env="client")
8
storeRowRecordAction: function(id: string, obj1: json): UiSdlReduxAction
9
10
@typeScript(env="client")
11
@uiSdlReducer(actionType='ROW_RECORD_STORE')
12
rowRecordStoreReducer: private inline function(state: !UiSdlReduxState, action: UiSdlReduxAction): UiSdlReduxState
13
}
Copied!
1
import { setConfigInApplicationState } from '@c3/ui/UiSdlApplicationState';
2
3
export function storeRowRecordAction(id, obj) {
4
return {
5
type: id + '.ROW_RECORD_STORE',
6
payload: {
7
applicationStateId: id,
8
obj: obj
9
}
10
}
11
}
12
13
export function rowRecordStoreReducer(state, action) {
14
const appStateId = action.payload.applicationStateId;
15
return setConfigInApplicationState(appStateId, state, ['rowRecord'], action.payload);
16
}
Copied!

Access your application state

In order for your components to have access to application state you must:
  1. 1.
    Create the a JSON file with the application state type.
  2. 2.
    Add the application state to the content json file.

Step 1 - Create the a JSON file with the application state type.

Under ui>c3>meta, create a file for your application state:
1
// exampleUI.UiSdlApplicationStateEUI.json
2
{
3
"type":"myApplicationStateCollections"
4
}
Copied!
The myApplicationStateCollections refers to the type you declared that holds all of the reducers and actions. The code block myApplicationStateCollections refers to the application state Type that is created in the in the previous example: myApplicationStateCollections.c3typ.

Step 2 - Add the application state to the content json file.

To allow components to have access to application state, you must incorporate the field: applicationStateRef.
For example, the a modal might want to read from the application state to grab specific data that is rendered when the page is loaded. In order for a modal file to have access to the application state, you incorporate the applicationStateRef in the exampleUI.HomeContent.json file.
applicationStateRef
exampleUI.HomeContent.json
1
"applicationStateRef": {
2
"id":"exampleUI.UiSdlApplicationStateEUI.json"
3
}
Copied!
1
{
2
"type": "UiSdlConnected<UiSdlLayoutBase>",
3
"applicationStateRef": {
4
"id":"exampleUI.UiSdlApplicationStateEUI"
5
},
6
"component": {
7
"children": [
8
{
9
"id": "exampleUI.customTableTypesBasic"
10
},
11
{
12
"id": "exampleUI.customHeaderModal"
13
}
14
]
15
}
16
}
Copied!
This file now has access to the application state. Best practice: Place the application state inside the content file instead inside the view comment modal.
Best Practices: The Application State is included in content json files instead directly incorporating it into components.

Epics

Epics call and use actions and reducers to read and write to the state. To Epics are written to perform function epic(action, state) => new state.
You must include the word Epic in the file name when creating an epic. To declare a new custom epic, you must declare the epic in a c3typ file and must mixes with UiSdlEpic. To create the new custom epic, you must create the epic in a ts file.
Epics write and read into the state and grab data from the state by calling actions and reducers.
Epics use actions to store information into the application state.
For example:
1
...
2
import { storeRowRecordAction } from '@c3/ui/myApplicationStateCollections'
3
4
// Overrides the epic method
5
export function epic(actionStream, _stateStream) {
6
return actionStream.pipe(
7
mergeMap(function (action) {
8
...
9
return concat(
10
...
11
of(storeRowRecordAction(...))
12
);
13
}
14
)
15
)
16
}
Copied!
Epics can also grab information from the state; epics call getConfigFromApplicationState(applicationState, state, reducer) .
To use getConfigFromApplicationState, you must import the function:
import {getConfigFromApplicationState} from '@c3/ui/UiSdlApplicationState';
myApplicationStateCollections.c3typ
myApplicationStateCollections.ts
1
@typeScript
2
type myApplicationStateCollections mixes UiSdlApplicationState {
3
/*
4
* Listens for actions of type 'ROW_RECORD_STORE'
5
*/
6
@typeScript(env="client")
7
@uiSdlReducer(actionType='ROW_RECORD_STORE')
8
rowRecordStoreReducer: private inline function(state: !UiSdlReduxState, action: UiSdlReduxAction): UiSdlReduxState
9
10
@uiSdlActionCreator(actionType='ROW_RECORD_STORE')
11
@typeScript(env="client")
12
storeRowRecordAction: function(id: string, obj1: json): UiSdlReduxAction
13
}
Copied!
1
import { setConfigInApplicationState } from '@c3/ui/UiSdlApplicationState';
2
3
export function rowRecordStoreReducer(state, action) {
4
const appStateId = action.payload.applicationStateId;
5
return setConfigInApplicationState(appStateId, state, ['rowRecord'], action.payload);
6
}
7
8
export function storeRowRecordAction(id, obj) {
9
return {
10
type: id + '.ROW_RECORD_STORE',
11
payload: {
12
applicationStateId: id,
13
obj: obj
14
}
15
}
16
}
Copied!
In Custom Epics and Custom Reducers, we create a dynamic header in a modal that deletes an event. This is a great way to showcase how to create custom epics, custom reducer and actions and how they interact with each other.

Additional Resources

To find built-in actions, reducers and epics, go to the documentation page in Developer Portal or run command: c3ShowType(<UISdlComponentName>) and click on the Index tab.