Ext JS to React: Mixins

   JavaScript

This is part of the Ext JS to React blog series. You can review the code from this article on the Ext JS to React Git repo.

As your applications grow, you’ll want to reuse code where possible. Instead of writing a fetchUser method on each component requiring the current user’s metadata, you’d write it once and then share it to the classes that need it. Ext JS made extensive use of mixins within the framework itself and provided a public mixin API for all Ext JS classes. In this article, we’ll look at how the same concept of shared code can be accomplished in React. To start, let’s first take a look at how to mix one class into another within Ext JS:

Ext JS Mixins

Ext.define('Foo', {
    mixins : [
        'Ext.mixin.Observable'
    ],
    
    constructor : function (config) {
        this.mixins.observable.constructor.call(this, config);
    }
});

var instance = new Foo();

instance.fireEvent('foobar', instance);

Ext JS makes it easy to list the mixins you want on a class via the mixins array. For the Observable mixin, it also has a constructor method, so we have to call it in our class’ constructor. You can see when we create an instance of Foo, we now have the fireEvent method even though fireEvent is not defined in the Foo class. The method actually lives in the Observable mixin which is now applied onto the Foo class’ prototype.

HOCs to the rescue

Historically, React offered a React.createClass method with a mixins config option. The mixins option accepted an array of objects containing methods and properties to be applied to the class. However, React mixins are no longer supported with classes defined with ES6+ syntax. React has distanced itself from its own mixins solution in favor of higher-order components (HOC). Simply said, HOCs are functions that accept a React class as an argument and return another class. And there’s a lot of power in that. Using HOCs, we can effectively share methods and properties to any class we pass in.

HOCs add, remove, or change props passed to the supplied component. For example, let’s say that every component we want passed to the HOC should get a user prop. By having the user prop supplied by the HOC, all concerns relating to fetching and validating a user is offloaded from the components that will ultimately consume the user metadata.

withUsername.js

function withUsername(WrappedComponent) {
 return class extends React.Component {
   state = {
     user: {}
   }

   componentWillMount () {
     // assumes there is a utility getUserInfo()
     // function to fetch the logged in user info
     this.setState({
       user: getUserInfo()
     });
   }

   render () {
     return <WrappedComponent {...this.props} {...this.state.user} />;
   }
 }
}

The withUsername HOC can be used to inject a user prop into any passed component. In our HOC example, we pass only the component to be rendered as a param. However, the HOC pattern is simply a function that renders a new component and that function can have as many params as makes sense for your use case. For example, in our snippet above we pass in a component and pass a user prop to it. This is populated by a hard-coded getUserInfo() utility function. But, what if we wanted the HOC to be even more flexible allowing any user retrieval action to be used? We could define withUsername with two params in its signature where the second is a function used to fetch the user data.

function withUsername(WrappedComponent, getUserInfo) {

Decorator syntax

Before we move forward with additional examples, let’s take a moment to discuss the use of HOC wrappers using decorator syntax. Instead of explicitly calling the HOC with our component-to-be-wrapped like we did above (withUsername(WrappedComponent)), we can decorate the WrappedComponent class itself.

If we knew we wanted our Box component class to float, we could “decorate” it with @withFloat as we define it:

import withFloat from './withFloat';

@withFloat
class Box extends React.Component {
    // ...
}

Decorators are not yet a ratified JavaScript specification. However, with tools like Babel, we can take advantage of decorators today. The payoff is improved readability, particularly when adding multiple HOCs to a class.

Note: If you want to use decorators and are using create-react-app to develop your application, you will have to eject using the npm run eject command. This is required to add a plugin to Babel. After ejecting you may need to run npm install once more to update dependencies.

After ejecting (as necessary), install the Babel plugin using npm install --saveDev babel-plugin-transform-decorators-legacy and then add the following babel object in the package.json:

"babel": {
  "plugins": [
    "transform-decorators-legacy"
  ],
  "presets": [
    "react-app"
  ]
},

Decorators are now able to be transpiled by Babel and used within your project.

Masking components

Let’s take a look at a more practical scenario where we might want to “decorate” a component. Let’s say we want to make a component “maskable.” In Ext JS, the Component class had a mask() method that would mask off our component’s element and optionally display a message within the mask (often a loading message as remote content was fetched asynchronously). With HOCs, we’re able to impart component markup to our wrapped class. Let’s first take a look at a component we want to mask while content is loaded:

@withMasking
class App extends Component {
  static defaultProps = {
    onBeforeLoad: () => {},
    onLoad: () => {}
  }
  componentDidMount () {
    this.load();
  }
  load () {
    this.props.onBeforeLoad();
    // fetch remote content for this component
    // call onLoadEnd when done
    // we’re faking the request with a setTimeout()
    // to simulate the round trip time
    setTimeout(() => {
      this.props.onLoad()
    }, 3000); // 3 seconds
  }
  onLoad () {}
  render () {
    return <div style={{width: "200px", height: "200px" }}></div>;
  } 
} 

Masking HOC

In this oversimplified example, we define a class that calls its load method when mounted and renders a div. But, there’s no masking implementation to be found. Let’s create an HOC function to wrap our component with a maskable element and include the necessary masking logic:

function withMasking(WrappedComponent) {
  return class extends Component {
    state = {
      mask: this.props.mask,
      maskMsg: this.props.maskMsg
    }
    onBeforeLoad = () => {
      this.setState({
        mask: true,
        maskMsg: 'loading...'
      });
    }
    onLoad = () => {
      this.setState({
        mask: null,
        maskMsg: null
      });
    }
    render() {
      const { onBeforeLoad, onLoad, props } = this;
      const methodProps = { onBeforeLoad, onLoad };
      const { mask, maskMsg } = this.state;
     
      return (
        <div style={style.wrap}>
          <WrappedComponent {...methodProps} {...props} />
          {mask && <div style={style.modal}>
            {maskMsg && <div style={style.msg}>{maskMsg}</div>}
          </div>}
        </div>
      );
    }
  }
}
 

The HOC function sets up the state object (using props as defaults). The onBeforeLoad method sets the mask and maskMsg state used by the elements in the render method (with the onLoadEnd method nullifying the mask data). The render method wraps the passed-in component in a div used for masking. If a mask prop is passed, a masking element is rendered using the modal node of the style object. If the maskMsg prop is passed we include a text node as well. The ability to mask using props makes our withMasking HOC that much more flexible.

Masking style

For completeness, below is the CSS style used for masking. In this example we’ve included all styles in an object that lives in the same file as the withMasking HOC:

const style = {
 wrap : {
   display  : 'inline-block',
   position : 'relative'
 },
 modal : {
   position       : 'absolute',
   top            : 0,
   left           : 0,
   right          : 0,
   bottom         : 0,
   background     : 'rgba(0, 0, 0, .3)',
   display        : 'flex',
   alignItems     : 'center',
   justifyContent : 'center'
 },
 msg : {
   padding    : '8px 16px',
   border     : '1px solid #555',
   background : 'rgba(0, 0, 0, .1)',
   color      : '#333'
 }
};

 

Ext JS to React: Mixins -- Simple Decorators



Note: To fully enable testing, it’s wise to define and export both the target class and the HOC- decorated class. That way, the undecorated class can be tested independent of the decorating logic. However, it’s not possible to export both the enhanced and original class when using decorator syntax without decorating a class that extends the original class. That may be a consideration when choosing the pattern you use in your code since inheriting from user-classes is not preferred in React.

render props

HOCs are an established pattern that you’ll likely find in volume in existing code. However, a new pattern is emerging in addition to HOCs: render props. The render props pattern prevents collisions between props of the same name on both the HOC and the target component. render props components are composed directly in the application’s JSX the same as with other components. Additionally, it prevents the need to copy over any static methods you may have defined on the component passed to the HOC. The render props pattern can be seen used in the wild within the react-router and react-motion libraries.

Network view

Before we look at an example of render props in action, let’s create a simple <NetworkView> that will perform an onBeforeLoad, load, and onLoad action similar to the <App> view in our previous example:

import React, {Component} from 'react'

class NetworkView extends Component {
  state = {
    ip: this.props.ip,
    mask: this.props.mask,
    router: this.props.router
  }

  static defaultProps = {
    onBeforeLoad: () => {},
    onLoad: () => {}
  }
  componentDidMount () {
    if (!this.props.ip) {
      this.load();
    }
  }
  load () {
    this.props.onBeforeLoad();
    setTimeout(() => {
      this.setState({
        ip: '192.168.1.2',
        mask: '255.255.255.0',
        router: '192.168.1.1'
      });
      this.props.onLoad()
    }, 3000);
  }
  onLoad () {}
  render () {
    const { ip, mask, router } = this.state;

    return (
      <div style={{width: "300px", padding: "12px" }}>
        <h1>TCP/IP</h1>
        IP: {ip}<br/>
        Network Mask: {mask}<br/>
        Router: {router}
      </div>
    );
  } 
}

export default NetworkView; 

Our <Network> component accepts five props: ip, mask, router, onBeforeLoad, and onLoad. Here we see the first benefit of using the render prop pattern. Our Network component and our masking component, which we’ll take a look at next, both accept a mask prop. The HOC pattern would have introduced a conflict between these props that we would need to account for. The render props pattern circumvents this conflict in prop names altogether, since both the wrapping and the wrapped component are composed in the JSX separately.

Masking class

Let’s take a look at the <Mask> component we’ll use to mask our Network component’s HTML:

import React, { Component } from 'react'

const style = {
  wrap : {
    display  : 'inline-block',
    position : 'relative'
  },
  modal : {
    position       : 'absolute',
    top            : 0,
    left           : 0,
    right          : 0,
    bottom         : 0,
    background     : 'rgba(0, 0, 0, .3)',
    display        : 'flex',
    alignItems     : 'center',
    justifyContent : 'center'
  },
  msg : {
    padding    : '8px 16px',
    border     : '1px solid #555',
    background : 'rgba(0, 0, 0, .1)',
    color      : '#333'
  }
};

class Mask extends Component {
  state = {
    mask: this.props.mask,
    maskMsg: this.props.maskMsg
  }
  onBeforeLoad = () => {
    this.setState({
      mask: true,
      maskMsg: 'loading...'
    });
  }
  onLoad = () => {
    this.setState({
      mask: null,
      maskMsg: null
    });
  }
  render() {
    const { onBeforeLoad, onLoad } = this;
    const { mask, maskMsg } = this.state;
   
    return (
      <div style={style.wrap}>
        {this.props.render({onBeforeLoad, onLoad})}
        {mask && <div style={style.modal}>
          {maskMsg && <div style={style.msg}>{maskMsg}</div>}
        </div>}
      </div>
    );
  }
}

export default Mask; 

Our Mask component looks almost identical to the class returned by the withMasking function we saw before. The primary difference is that this component renders child components using:

{this.props.render(onBeforeLoad, onLoad)}

Any JSX returned by the <Mask> render function has access to the params passed (onBeforeLoad and onLoad in this case). The convention is to use the prop “render“, but in reality it could be any prop name you choose. And the params passed to the render prop are completely flexible as well. Here we pass down methods from our Mask component, but we could have passed in the Mask component’s state, props, or any other value benefitting the render output.

Masking applied

Let’s take a look at the <Mask> and <NetworkView> in action:

import React from 'react';
import Mask from './Mask';
import NetworkView from './NetworkView';
import './App.css';

const App = () => (
  <Mask render={({onBeforeLoad, onLoad}) => (
    <NetworkView
      onBeforeLoad={onBeforeLoad} onLoad={onLoad}
    />
  )}/>
);

export default App; 


Ext JS to React: Mixins -- Render Props



The masking abstraction works similar to the HOC masking pattern we saw before, but with no prop collision risk and composed neatly in our <App> JSX. We therefore remove the need for a dedicated class to wrap our view with the shared logic component as seen with HOCs (i.e. export default withMasking(App)).

The React docs outline the basic usage and benefits of the render props pattern. Once you see its elegance, we believe you’ll want to incorporate the pattern in your own code going forward. For a deeper look at the same example contrasted against the now-historic mixins option and the HOC pattern, we recommend watching the presentation on render props from react-router’s own Michael Jackson.

Wrap up

HOCs and render props are powerful utilities in an application, though they can be a bit confusing. As you get more comfortable with React code, the use cases for HOCs and / or render props will become more obvious, and you’ll find yourself at home with their more common patterns. Maintaining reusable code will enable your projects to grow with less maintenance overhead as your applications evolve and change over time. As you come across novel and useful HOC and render prop examples in your own coding experience and research, add your findings in the comments section of this post!


Like What You See?

Got any questions?