Integrating React components with complex legacy forms

Everyone wants to work with exciting new JavaScript libraries such as React - but how do you integrate it with your existing website? In this post I will walk through some of the techniques used integrating a new React component with a complex existing form on our website.

A surprisingly complex form

uSwitch is the number one energy supplier switching service in the UK. To complete the switching process our users have to fill out a surprisingly complex form with their personal details, address history, energy meter numbers and bank account details. The form was built several years ago using the then standard stack of Ruby on Rails on the server side rendering HTML with a combination of jQuery, lodash and JST templates on the client side.

After a few years of user interface iteration and additional requirements the client side architecture was struggling. More functionality was present on the client side but the original progressive enhancement and DOM manipulation style approach was hard to maintain, buggy and slightly scary to change. Thankfully in the meantime a whole host of JavaScript frameworks and libraries had become popular...

Why we chose React

We investigated a few Javascript frameworks (such as Angular and Backbone) but they seemed a poor fit to our type of web application. First they focus on Single Page Applications - but our website is still fundamentally a form based multipage journey. We wanted to be able to make incremental improvements to our pages whilst avoiding the 'big rewrite'. Second, they enforce a highly structured MVC approach at the cost of a fair amount of additional boilerplate code. As our client side code was primarily focused on view concerns this additional plumbing code didn’t seem to add much value.

React seemed a good fit for our use case. Firstly, it is a library, not a framework and doesn’t force a rigid structure on the existing code. Secondly, React’s virtual DOM and explicit state approach leads to far simpler view layer code. Thirdly, it is possible to integrate components in an existing form (as we will discuss below).

A React address entry component

We chose to rewrite the most complex and troublesome part of the form, the address entry component. This part of the page allows users to search by postcode for their address (with an address list populated via Ajax), select their address from the list or enter their address manually. This was straightforward to model in React with four React subcomponents for each view of the user interaction.

The four different views of the address entry control

The interesting part was integrating the component with the rest of the form. This required:

Adding multiple instances of the address component to the form (as some energy suppliers require up to three years of address history)

Synchronising validation of the React component with the rest of the form

Updating the non-React parts of the form based on user interaction with the React component

Getting the information entered in the React component to the server

How the Page's JavaScript interacts with the React component

Adding multiple instances of the React component to the form

When the server renders the HTML a placeholder div element is included in the form for the address component. On page load the page's JavaScript calls React.renderComponent to render the address component into the div.

The props used to initialise the component (which include an initial list of possible addresses and any previously entered address information) are serialised to JSON on the server and embed into the script tag on the page. This ensures that if the page is submitted and reloads after failing server validation the component is reinitialised with any data the user has previously entered.

To complicate things further the users may be required to enter up to four addresses (the address the energy is supplied to, a different billing address and up to two previous addresses) based on the answers to other questions on the form! To handle this the Page’s JavaScript responds to events on these inputs and calls React.renderComponent or React.unmountComponentAtNode to add or remove the React components from the page.

Validation of the React components

When the user submits the form we need to validate that they have entered an address correctly. This validation is handled inside the React component. To instigate this we call a custom validate method on the component instance (RenderComponent return a reference to the component instance). If any component instance returns an error the page's JavaScript cancels the form submission.

Getting user input from React to the server

To post the data back to the server the React component simply renders inputs elements inside the form on the page. When the user chooses to enter an address manually the inputs are shown in the form. When an address is selected from the address list we serialize the selected address into the form inputs and hide the form. Additionally the inputs are changed to hidden inputs to remove them from the tab order. (A gotcha here is that you can’t change input type in Internet Explorer 8, so we use browser detection to skip this step on that browser) As there can be multiple address components on the page each instance of the component uses a unique prefix for the input name.

Communicating with the rest of the form

One final complication is that we need to ask the user additional questions in the non React part of the form based on which address is selected from the React code. To do this when an address is selected by the use we trigger an event. The page's JavaScript listens for this event and updates the non React elements of the form accordingly.

No magic - just straightforward integration

We now have React running smoothly on a critical part of our website. Although integrating React components with an existing form requires additional work there is no magic involved - just standard JavaScript integration techniques as outlined above. For us the ability to incrementally adopt React in this manner is a huge plus of the library.