# Managing Component State

Many JourneyApps UI components maintain internal **state** that controls pagination, search, selection, sorting (where applicable), and more.\
The two components that offer the most comprehensive state management features are:

* `object-table` — tabular data display with sorting, filtering, editing, selection, etc.
* `list` — card-style or simple item listing with pagination, search, and single selection.

{% hint style="warning" %}
**Managing state is considered an advanced topic.**\
In most cases, both components work very well using **default behavior** without any manual state control.
{% endhint %}

#### Shared Concepts

Both `object-table` and `list` support the same three main mechanisms for reading and controlling state:

1. **`on-state-change`**\
   Called whenever the user interacts with the component in a way that changes its state (paging, searching, selecting, etc.).
2. **`init-state`**\
   Sets the **initial** state when the view is loaded (very useful for preserving user preferences when returning to a view).
3. **Programmatic `setState()`** via JavaScript/TypeScript\
   `component.objectTable({ id: "…" }).setState(…)` or `component.list({ id: "…" }).setState(…)`\
   Allows updating state after the view has loaded (e.g. reset filters on button press, jump to page X, clear search).

**When to prefer one method over the other**

* Use **`init-state`** for most cases — it runs once on view load and is perfect for remembering pagination/search/selection when users navigate back.
* Use **`setState()`** only when you need to change state dynamically **after** the view has loaded (e.g. button resets table, syncs two components, etc.).

**Supported components**\
Currently only the following components expose this state management API:

* [`object-table`](https://docs.journeyapps.com/reference/build/ui-components/all-ui-components/object-table)
* [`list`](https://docs.journeyapps.com/reference/build/ui-components/all-ui-components/list)

***

Data displayed in an `list`  and  `object-table` is controlled by its state. The objects provided below list all of the properties which can be safely accessed and mutated from JavaScript/TypeScript.

{% hint style="warning" %}
**Advanced topic**

Managing state is considered an advanced topic. In most cases a component can be used effectively without directly managing its state.
{% endhint %}

### `on-state-change`

The function specified in [`on-state-change`](https://docs.journeyapps.com/reference/build/all-ui-components/object-table#on-state-change) is called when the state of the table or the list is changed due to a UX/UI interaction such as changing to the next page. The `$state` parameter can be passed to the function which contains a simplified JavaScript/TypeScript object of the component's state.

{% tabs %}
{% tab title="object-table" %}

```xml
<object-table on-state-change="$:tableStateChange($state)">
    ...
</object-table>
```

{% code title="main.ts" %}

```javascript
function tableStateChange(tableState){
    // tableState contains the table's current state
    console.log(JSON.stringify(tableState));
}
```

{% endcode %}
{% endtab %}

{% tab title="list" %}

```xml
<list on-state-change="$:listStateChange($state)">
    ...
</list>
```

{% code title="main.ts" %}

```javascript
function listStateChange(listState){
    // list contains the list's current state
    console.log(JSON.stringify(listState));
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

The internal structure of `$state` is as follows:

{% tabs %}
{% tab title="object-tabe" %}
{% hint style="info" %}
The `validationErrors` field is available from Runtime `4.90.9+`. It is handled internally and therefore cannot be set by the developer via the `setState` or `init-state` logic.
{% endhint %}

{% code title="main.ts" %}

```typescript
interface State{
  filters: {
    /**
     * Contains the payloads for the filters that are applied. Note that each type of filter is unique, and is identified
     * using the `factory` variable
     */
    [key: string]: {
      factory: string;
      
      /**
       * current evaluated title of the column
       */
      column: string;
    }
  };
  /**
   * the global search applied on the table
   */
  search: string | null;
  /**
   * current limit applied to the table (might be user-adjustable in the future which is why it is available here) 
   */
  limit: number;
  /**
   * Current page of the table, starts at 1
   */
  page: number;
  /**
   * All the columns being sorted, and in the sort order.
   */
  sorting: Array<{
    /**
     * current evaluated title of the column
     */
    column: string;
    key: string;
    type: 'asc' | 'desc' | 'none';
  }>;
  /**
   * Which cell is currently in edit mode, or null otherwise
   */
  editing: {
    id: string;
    column: string;
  } | null;
  /**
   * An array of cells which are currently selected (which can include clusters of selected cells)
   */
  selected: Array<{
    id: string;
    column: string;
  }>;
  /**
   * Indicates if the table is in full screen mode or not
   */
  fullscreen: boolean;
  /**
   * Callouts which are currently visible in the table.
   */
  callouts: Array<{
    message: string;
    color: string;
    id: string;
    column: string;
  }>
  /**
   * Validation errors currently present on the table
   * (Available from Runtime 4.90.9+)
   */  
  validationErrors: Array<{
    message: string;
    id: string;
    column: string;
  }>
}
```

{% endcode %}

{% hint style="warning" %}
**column properties**: The state payload will in contain `column` properties which refer to the current name of the column. This is provided as convenience data to make working with the state easier, but it is not used when initializing state since the value can change dynamically. Using the `column` data to initialize the state is therefore considered **unsafe** and should not be relied on.
{% endhint %}
{% endtab %}

{% tab title="list" %}
{% code title="main.ts" %}

```typescript
interface ReturnState {
  /**
   * The global search applied on the list
   */
  search?: string;
  /**
   * Current limit applied to the list (might be user-adjustable in the future
   * which is why it is available here)
   */
  limit?: number;
  /**
   * Current page of the list, starts at 1
   */
  page?: number;
  /**
   * The index of the currently selected item (1-based)
   */
  selectedItem?: number;
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### `init-state`

A table or list can initialize its internal state after the component is loaded using a similar structure as `on-state-change`.

How [`init-state`](https://docs.journeyapps.com/reference/build/all-ui-components/object-table#init-state) differs from `on-state-change`:

* &#x20;For the `object-table` components, `init-state` will ignore `column` fields (it only uses column `key` information)

{% tabs %}
{% tab title="object-table" %}

```xml
<object-table init-state="$:initState()">
  ...
</object-table>
```

{% code title="main.ts" %}

```typescript
function initState(){
    /**
     * @type ReturnState
     */ 
    return { page: 1, fullScreeb: true, ... }
}
```

{% endcode %}
{% endtab %}

{% tab title="list" %}

```xml
<list init-state="$:initState()">
  ...
</lsit>
```

{% code title="main.ts" %}

```typescript
function initState(){
    /**
     * @type ReturnState
     */ 
    return { page: 1, limit: 100, ... }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

The internal structure of the returned `state` object is as follows:

{% tabs %}
{% tab title="object-table" %}
{% code title="main.ts" %}

```typescript
interface ReturnState{
  filters: {
    /**
     * Will contain the payloads for the filters that are applied. Note that each type of filter is unique, and is identified
     * using the `factory` variable
     */
    [key: string]: {
      factory: string;
    }
  };
  /**
   * the global search applied on the table
   */
  search: string | null;
  /**
   * current limit applied to the table (might be user-adjustable in the future which is why it is available here) 
   */
  limit: number;
  /**
   * Current page of the table, starts at 1
   */
  page: number;
  /**
   * All the columns being sorted, and in the sort order.
   */
  sorting: {
    key: string;
    type: 'asc' | 'desc' | 'none';
  }[];
  
  /**
   * Indicates whether the table is in a full screen state or not  
   */
  fullscreen: boolean;
}
```

{% endcode %}

{% hint style="info" %}
**Note:** The `validationErrors` field is handled internally and cannot be set by the developer.
{% endhint %}
{% endtab %}

{% tab title="list" %}
{% code title="main.ts" %}

```typescript
interface ReturnState {
  /**
   * The global search applied on the list
   */
  search?: string;
  /**
   * Current limit applied to the list (might be user-adjustable in the future
   * which is why it is available here)
   */
  limit?: number;
  /**
   * Current page of the list, starts at 1
   */
  page?: number;
  /**
   * The index of the currently selected item (1-based)
   */
  selectedItem?: number;
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Changing the state from JavaScript/TypeScript

To change the state of the table or list using JavaScript/TypeScript, make use of the `component.objectTable({ id: "my-id" }).setState({ ... })` method or `component.list({ id: "my-id" }).setState({ ... })`. This method requires an `id=""` attribute to be present on the `object-table` that matches the `id` used in above `component.objectTable()`.

{% hint style="info" %}
**Note:** The `setState()` method takes the exact same payload as `init-state=""`.
{% endhint %}

For example:

{% tabs %}
{% tab title="object-table" %}
{% code title="main.ts" %}

```typescript
function updateState(){
    component.objectTable({ id: "my-table" }).setState({ page: 1, search: ''});
}
```

{% endcode %}

```xml
<object-table id="my-table">
    ...
</object-table>
```

{% endtab %}

{% tab title="list" %}

```typescript
function updateState(){
    component.list({ id: "my-list" }).setState({ page: 1, search: ''});
}
```

```xml
<list id="my-list">
    ...
</list>
```

{% endtab %}
{% endtabs %}

**When to use `setState()` vs `init-state=""`**

Since both of these methods do the same thing but in different parts of an application, it is important to understand why and when you should use one over the other. In most JourneyApps applications, `init-state=""` is the easiest to use because it is called only once when the view loads. This is particularly useful when users navigate using a table, and when they return to the table they expect it to look exactly the same as before.

`setState()` is useful when a developer needs to control or manipulate the table after the view has loaded. Actions such as changing the page and updating the select cell are events that should not fire on every update on a view, but rather when very specific events occur, such as pressing a particular button.

{% hint style="info" %}
**Tip:** Implementing `setState()` can slow down your view if done frequently. Use it only where `init-state=""` is not feasible.
{% endhint %}

***
