We thought many customers might be interested in a sample like this, so we created a Dynamic Dashboard sample and made that available to our users. Since then, we received a significant amount of feedback, including interest in the drag-drop implementation, touch support, and component design.

The original sample was implemented using Wijmo and Angular 1.x. When we released our Wijmo React interop a few weeks ago, we thought this would be a great React sample as well, and decided to create a React-based version of the sample.

The React version of the sample looks exactly like the original, but the implementation is quite different and illustrates some interesting aspects of React development.

React Implementation and TSX files

The React version of the Dynamic Dashboard application is a TypeScript application. It uses TSX files which are the TypeScript equivalent of React JSX files.

Each TSX file typically contains one React component. Adding TSX files to Visual Studio projects is trivial. Right-click the project node in the Solution Explorer, select “Add” and then “TypeScript JSX File”. You can then write regular TypeScript code with the usual DOM-like extensions typical of React code.

The tooling provided by Visual Studio for React and TypeScript is excellent. You don’t have to waste any time worrying about configuration and can get right into coding, at which point you will get all the design-time error checking and productivity tools you are used to.

The screen below shows the project with the TSX files and the options offered by Visual Studio to fine-tune the handing of TSX files should you want to do so:

Application Architecture

The Dynamic Dashboard app has two types of components: the dashboard frame and the tiles in it.

The dashboard frame is a stateful component. It defines a catalog of tile types available and a list of the tiles that make up the current dashboard. It also provides logic for adding, removing, and reordering the tiles.

The tiles have no state (in the React sense). They receive data from the frame via properties (passed in as element attributes). Tiles are quite simple; the app defines nine type of tiles, including various charts, gauges, bullet graphs, and a grid.

In a traditional OOP application, all tiles would extend a base tile class. In React (and many other JavaScript frameworks), you are encouraged to use composition instead of inheritance. I see this as a limitation. Composition and inheritance are different mechanisms, each has its uses, and one does not fully replace the other.

In our app, we followed the React team’s advice and implemented our tiles using composition rather than inheritance. We define a Tile component that provides the tile border (with a header and buttons). The Tile component has a “content” property that defines the actual tile content.

Dashboard Frame Component

The “App” component implements our dashboard frame. It is instantiated with the following standard snippet of HTML in application’s “default.htm” file:

tileCatalog: list of tile types that are available and can be added to the dashboard.

tiles: list of the tiles currently on the dashboard.

key: identifier to use when creating the next tile (more on this later)

data: data to be shown on the dashboard (passed as a prop to all tiles)

Let’s move on to the component’s render method to see how the state is used to generate the dashboard.

We start by rendering a header with the application title and a logo. Notice how the TSX syntax looks a lot like HTML, with some minor differences. Instead of “class”, for example, we must use “className”. That is because although it looks like HTML, this is really JavaScript code, and the DOM element’s property is called “className”, not “class”, The same applies to the “for” attribute of label elements, which in TSX must be specified as “htmlFor”.

Next, we render a section that contains a drop-down where the user can select the type of tile he wants and a button to add the tile to the dashboard.

We use a Wijmo ComboBox to show the tile catalog and to keep track of the tile type currently selected. The itemsSource and displayMameberPath properties are used to populate the list and to determine which property of the items should be displayed on the list. The current tile type is exposed by the catalog’s currentItem property. Next to the combo, there is a button that invoked the “addTile” method on the component:

The method starts by making a copy of the current tiles array and incrementing the key used to uniquely identify tile instances. It adds a new item to the tiles array with the name specified by the catalog’s currentItem, and calls setState to update the state with the new tiles array.

The call to setState causes React to update the UI showing the updated tiles array.

The code that renders the dashboard tiles is typical React. Is uses the array.map method to generate a list of components that make up the UI. In this case, the components are instances of Tile following properties:

header: name of the tile (displayed in the tile header).

content: component that represents the tile content (e.g. a chart or gauge).

remove: a callback invoked when the user clicks the “delete” button on the tile header.

Note that the key and index parameters are used for different purposes. The index parameter is used by the App component to identify tiles in the tiles array, specifically when deleting tiles. The index of a tile may change if tiles are removed or reordered. The key parameter, on the other hand, is used by the React framework to establish a mapping between array elements and components. The key does not change when tiles are removed or reordered.

The removeTile method acts as an event handler. It is called by the Tile component when the user clicks the delete button on the tile header. The code uses the array.filter method to select all tiles except the one that was clicked, and calls setState to update the state with the new tiles array.

As before, the call to setState causes React to update the UI showing the updated tiles array.

The render method creates a frame with a header and content determined by the header and content properties. The header includes a “remove” span that when clicked invokes the remove callback which is also a property (this.props.remove).

Notice how the outer div has its draggable attribute set to true. This enables HTML drag/drop operations, which we will discuss later.

The Tile component’s most interesting property is content, which represents the child component that contains the tile information. Our application defines nine types of tile content component, including charts, gauges, grid, etc. They all have a data property that is set by the parent component and contains the data to be shown on the tile.

Tile components may be as simple or complex as you like. Adding new tile types is easy: create the component to show the data in “props.data” in whatever way you want, and add the new tile type to the application component’s tileCatalog array.

Tile components may contain other components, including Wijmo data visualization controls. For example, here is the implementation of the BarChart tile component:

The main advantage of the React implementation of this app is the neat encapsulation of the tile components. Each one is represented by a single TSX file that contains the logic and UI.

Dragging Tiles

We have shown how users can customize the dashboard by adding new tiles or removing existing ones. The part that is missing is the use of drag and drop to reorder tiles, which was one of the main initial requirements for the app.

We already had this code written for the Angular version of the app. Fortunately, the code used standard HTML5 drag/drop functionality which was very easy to port to React. This was done by moving the original drag/drop code into a method called enableItemReorder and calling that from the main component’s componentDidMount method:

Because the HTML drag/drop events work with the actual HTML elements on the DOM (and not with the React virtual DOM), we must update the state when a drop operation is finalized. This is done when handling the “drop” event, as shown in the code snippet below:

The final piece is touch support. Since the code relies on native HTML5 drag/drop events, we added drag/drop touch support simply by including the DragDropTouch polyfill in our project.

The screenshot below shows the running application, with a tile being dragged into a new position:

Conclusion

Porting the Dynamic Dashboard sample to React was surprisingly easy given the relative complexity of the app. Creating new tiles of arbitrary types, supporting drag/drop operations, and encapsulating arbitrary components within tiles are not trivial tasks.

In my opinion, the most significant benefits of using React in this sample were:

The ability to use TSX and get full design-time error checking, IntelliSense, and rich debugging for the entire app (including the TSX markup!).

The clean state-driven architecture imposed by React (which may take a little getting used to, but tends to pay off later).

The ability to encapsulate tiles neatly into single-file components.

The Dynamic Dashboard sample could be used as a starting point for actual dashboard apps. Obvious missing features are:

Bernardo de Castilho leads GrapeCity's technical direction as the company's Chief Technology Officer. Before gaining more than 20 years of experience in the software industry, Bernardo earned a doctorate in Civil Engineering at UC Berkeley. He loves the diversity of a global company like GrapeCity, as well as the challenges and freedoms that come with working on the cutting edge of software development. When he's not working, Bernardo enjoys travelling, biking, and scuba diving.