Using React in Mendix Widgets

on January 19, 2017

Share:

By leveraging React in the views for your custom Mendix widgets, you can compose user interfaces that are rich, beautiful and complex.

React is a JavaScript library for building user interfaces. It leverages component-based development to allow faster, more modular creation and organization of assets. It allows developers to control the look and feel of any part of a user interface when it is in a certain state, and encapsulate all those different renderings inside the component itself, which can then be combined with other components to build beautiful, complex user interfaces. The question I’ll be answering in this blog is: How can you best leverage React in Mendix?

Mendix + React = Fast, Beautiful, Elegant

When building enterprise web applications, component-based development is a major asset for developers to have in their back pockets. It ensures that specific guidelines and best-practices are followed, and that areas of the application with similar functionalities behave in a consistent fashion so that the experience is intuitive to the user. While Mendix provides a light-weight solution for component-based development in the form of Snippets, they cannot be nested or accessed programmatically, and they have no concept of state.

Preparing Your Widget Files

The Mendix widget framework does not natively support JSX (an XML-Like Syntax Extension for JavaScript), so if you’re opting to use JSX in your widget (not necessary, but a nice utility), be sure to have a transpiler rigged up. While I won’t be going into transpilers in this blog post, you can find an example of how to make this work using a modified mendix-generator on my GitHub at https://github.com/cdcharlebois/generator-mendix. The rest of this post will focus on a “pure-JS” implementation.

Initializing the React Component from Dijit

In a regular Mendix widget (created using the Yeoman generator) you’ll have the standard Dijit lifecycle methods. Follow these steps to initialize your React component:

First, include the React and ReactDOM libraries (from here and here) in your widget’s lib directory like this:

 

//my dependencies

'MyWidget/widget/lib/react',
'MyWidget/widget/lib/react-dom',

//...

 

Then, in your widget’s update lifecycle method, include the ReactDOM.render call, passing any necessary props to your root component.

update: function(obj, callback) {
    logger.debug(this.id + '.update');
    this._contextObj = obj;
    if (this._contextObj) {
     ReactDOM.render(
      React.createElement(ReactRoot, {person: this._contextObj}, null),
      this.widgetBase
     )
    }
    callback();
}

You can now build your React components.

// ReactRoot.js
define([
 ‘MyWidget/widget/lib/react’,
 ‘MyWidget/widget/lib/react-dom’,
 ‘MyWidget/components/js/Child’ // import any components needed here
], function(React, ReactDOM, Child) {
    return React.createClass({
     render: function(){
      var title = ['Hello', 'Greetings', 'Hola'][Math.floor(Math.random()*3)]
          + ' from React'
      return (
       React.createElement(‘div’, null, 
         React.createElement(‘h3’, null, title), 
         React.createElement(Child, {person: this.props.person})
       )
      )
     }
   })
 });

The above code is a simple example of a React component that renders a div with a random heading and then another React component inside it.

Notice that you had to follow the Dijit/AMD module loading pattern to ensure that your root is aware of your child component, Child.js.

You must follow this pattern with all the components that you will use. Define the module by first pulling in the React and ReactDOM libraries follow by any nested components.

Reading Data from and Writing Data to your Mendix App

Next, let’s look at an example of a simple component and talk about how to integrate the component with real data from your Mendix app.

 

Defining the Initial State with getInitialState

In a React component, the initial state is defined in the getInitialState function, and here, you’re defining your state as having a list, a string for your newPetName, and a string for your newPetType, defaulted to “cat”.


// The state is what triggers the real-time DOM updates
// ---
// Here we're just initializing it with some default values
getInitialState: function() {
   return {list: [], newPetName: '', newPetType: 'cat'}
},

 

Fetching Data with componentWillMount

componentWillMount is invoked immediately before mounting occurs, and you can use it to fetch data from your Mendix application to use in your component.

// componentWillMount is called immediately before the component is
// rendered to the DOM
// ---
// Here, you call a microflow to fetch your data, and store that in the
// state
componentWillMount: function() {
   var self = this
   mx.data.action({
   params: {
      actionname : 'TestSuite.DS_ReturnList',
      applyto    : 'selection',
      guids      : [this.props.person.getGuid()]
   },
   callback: function(res) {
      console.log(res)
      self.setState({list: res})
   },
   error: function(err) {
      console.log(err)
   }
 }, this)
},

 

Saving Data to the Server

In this component, you have a couple of listeners that are contacting the server. The first is invoked when a user creates a new pet. It creates the object on the server and then immediately updates your component’s state so that the UI updates immediately. Ideally, this action doesn’t have to talk to the server at all, and just modifies the state until the user is finished and is ready to save the data.

The second is invoked when the user has finished modifying data, and sends the list of new objects to the server to a microflow that saves them.

// Fired when the user is finished adding new records
// -----
// Use this to run the commit microflow and persist the data from the
// server to the DB
handleSubmit: function() {
   var self = this
   , pets = self.state.list.map(function(p){return p.getGuid()})
   console.log(pets)
   mx.data.action({
      params: {
      actionname : 'TestSuite.IVK_SavePets',
      applyto    : 'selection',
      guids      : pets
   },
   callback: function(res) {
      console.log(res)
   },
   error: function(err) {
      console.log(err)
   }
 })
},

Further Exploration

Instead of connecting to microflows in the componentWillMount and handleSubmit functions, you could connect to an exposed REST API from the Mendix app, which would add another layer of abstraction between your widget and the app, and make your widget more reusable. The full version of Child.js is included at the end of this page.

Conclusion

This is not a fully developed solution, but will provide some options for customers who are looking to leverage existing React components in a Mendix app.

The ability to decouple the data that drives the component’s rendering from the actual rendering itself allows developers to create self-contained components and user interfaces that react in real time to users’ input.

By including React in custom widgets, you can easily create user interfaces that are fast, rich, and beautiful. All the data and processing for visual updates can be handled on the client, and changes pushed to the server when the user is satisfied.

  • Download the the full child.js file here.

Subscribe to Our Blog

Receive Mendix platform tips, tricks, and other resources straight to your inbox every two weeks.

RSS Feed of the Mendix Blog
Conner Charlebois

About Conner Charlebois

Conner Charlebois is a Senior Solution Consultant at Mendix; part of the Expert Services team. He leads customers through digital transformation by implementing and executing new development and innovation practices.

| Community Profile
  • Amadeo Brands

    Good article will test this out in my next project Thank you