Skip to main content

Interact with the state of ActiveUI

This page shows how to interact with the state of ActiveUI. In particular, you will learn how to:

note

The snippets in this page work with the ActiveUI starter. If you have not installed it yet, then read Setup.

note

ActiveUI uses Redux for state management. A basic understanding of it is preferrable in order to understand what follows.

Consuming and updating the state of ActiveUI is possible using two hooks: useSelector and useDispatch from react-redux. It works just like when you use Redux to manage the state of any web application. The only difference is that you do not have to create and provide a store, as ActiveUI does it for you.

See more details and examples below.

Consume state attributes#

To retrieve attributes from the state of ActiveUI, use useSelector.

For instance, the following snippet implements a higher order component which depends on isPresenting. If it is true, then it just displays ActiveUI, but if it is false, then it also displays a custom horizontal application bar to the left of it.

import React, { ComponentType } from "react";import { useSelector } from "react-redux";import { getIsPresenting } from "@activeviam/activeui-sdk";
/** * HOC to be used around ActiveUI, in order to display a custom left bar (except in presentation mode). */export const withLeftBar = (WrappedApplication: ComponentType) => () => {  const isPresenting = useSelector(getIsPresenting);
  if (isPresenting) {    return <WrappedApplication />;  }
  return (    <div style={{ height: "100%", display: "flex" }}>      <div style={{ width: 200 }}>Custom left bar</div>      <div style={{ flexGrow: 1, position: "relative" }}>        <WrappedApplication />      </div>    </div>  );};

It can then be registered as follows, in your index.tsx:

  const extension: ExtensionModule = {    activate: async (configuration) => {+     configuration.higherOrderComponents = [+       ...(configuration.higherOrderComponents || []),+       withLeftBar+     ]    }  }

Like getIsPresenting in the example above, more selectors are exported from @activeviam/activeui-sdk and allow you to retrieve specific pieces of state:

Trigger state updates#

To update the state of ActiveUI, use useDispatch.

For instance, the following snippet implements an application menu item which adds a new empty page to the dashboard each time the user clicks it.

import React, { FC } from "react";import {  addPage,  ApplicationMenuItem,  DashboardPageState,  getDashboardState,  pluginWidgetPivotTable,} from "@activeviam/activeui-sdk";import { useDispatch, useSelector } from "react-redux";import MenuItem, { MenuItemProps } from "antd/lib/menu/MenuItem";
/** State of the added page */const addedPage: DashboardPageState = {  content: { "0": pluginWidgetPivotTable.initialState },  layout: {    children: [      {        leafKey: "0",        size: 1,      },    ],    direction: "row",  },  name: "New page",};
/** Menu item component */const AddPageMenuItemComponent: FC<MenuItemProps> = (props) => {  const dashboardState = useSelector(getDashboardState);  const dispatch = useDispatch();
  const handleClicked: MenuItemProps["onClick"] = (param) => {    if (dashboardState) {      // Update the state of ActiveUI in order to add a new page to the dashboard.      dispatch({        type: "dashboardUpdated",        dashboardState: addPage(dashboardState, { page: addedPage }),      });    }    props.onClick?.(param);  };
  return (    <MenuItem {...props} onClick={handleClicked}>      Add a page    </MenuItem>  );};
/** Menu item that can be wired into the ActiveUI configuration */export const addPageAppMenuItem: ApplicationMenuItem = {  component: AddPageMenuItemComponent,};

This menu item can be added into the Edit menu of ActiveUI as follows, in your index.tsx:

+ import { ApplicationMenu } from "@activeviam/activeui-sdk";
  const extension: ExtensionModule = {    activate: async (configuration) => {+     const editApplicationMenu = configuration.leftApplicationMenu[1] as ApplicationMenu;+     editApplicationMenu.children.push(addPageAppMenuItem);    }  }

For more information about the actions you can dispatch in order to update the state of ActiveUI, see Action.

Advanced use cases#

For more advanced use cases such as extending the state or changing the initial state, you can use a tool called a store enhancer.

Store enhancers allow to hook into ActiveUI's Redux store creation. They can be passed through Configuration, in your index.tsx:

+ import { StoreEnhancer, Reducer, PreloadedState } from "redux";
+ const myStoreEnhancer: StoreEnhancer =+   (createStore) =>+   (reducer: Reducer<any, any>, preloadedState?: PreloadedState<any>) => {
+     // ... This store enhancer does nothing ...+     // ... We will see more exciting examples below ...
+     return createStore(reducer, preloadedState);+   };
  const extension: ExtensionModule = {    activate: async (configuration) => {+     configuration.storeEnhancers = [+       ...(configuration.storeEnhancers || []),+       myStoreEnhancer,+     ];    }  }

Extend the state#

What if you need to share state (that is not part of the core ActiveUI state) between several components?

With the right store enhancer, your own state can live along the core ActiveUI state, within the ActiveUI store.

For instance, the following store enhancer introduces a boolean attribute called foo along with an action to toggle it. It allows your components to use useSelector and useDispatch in order to consume and update this slice of state.

import { PreloadedState, Reducer, StoreEnhancer } from "redux";
const fooReducer: Reducer = (state = true, action) => {  return action.type === "toggleFoo" ? !state : state;};
/** Store enhancer adding a slice of state called `foo` into ActiveUI */const myStoreEnhancer: StoreEnhancer = (createStore) => (  reducer: Reducer<any, any>,  preloadedState?: PreloadedState<any>,) => {  const enhancedReducer: Reducer<any, any> = (state, action) => {    const nextState = reducer(state, action);    const nextFoo = fooReducer(state?.foo, action);    return { ...nextState, foo: nextFoo };  };
  return createStore(enhancedReducer, preloadedState);};

Change the initial state#

You can configure the initial state of ActiveUI by overriding the Redux preloaded state. For instance, the following store enhancer makes ActiveUI start up in presentation mode:

/** Store enhancer allowing to start in presentation mode */const myStoreEnhancer: StoreEnhancer =  (createStore) =>  (reducer: Reducer<any, any>, preloadedState?: PreloadedState<any>) => {-   return createStore(reducer, preloadedState);+   const overriddenPreloadedState = {+     ...preloadedState,+     isPresenting: true,+   };+   return createStore(reducer, overriddenPreloadedState);  };
warning

Don't forget to forward preloadedState, as illustrated in the snippet above. Forgetting it can break other store enhancers, possibly registered by other ActiveUI extensions.