Running React only. jQuery is not defined |
Scope
Our production website was using an uncompressed and unminified 7000+ lines of jQuery file that do a lot of DOM manipulation.The site was slow and a lot of overhead with the javascript.
We recently migrated our site from jQuery to React with the following goals in mind.
We recently migrated our site from jQuery to React with the following goals in mind.
- Reduce file size
- Improve on load speed
- DOM rendering time
- Improve readability and maintainability of the code
React vs Angular
Perhaps one of the hardest decision I have to make is to choose between React and Angular. There are already many great articles talked about this topic and this is one of them.
The two technologies are pretty much head to head. My reason for React over Angular was more a cultural than technical reason.
The two technologies are pretty much head to head. My reason for React over Angular was more a cultural than technical reason.
Technical
Technology Stack
- Demandware
- React / Redux / Thunk / React Habitat
- ES6
- Webpack
Non-SPA
React is designed for Single Page Application, but our site is not. The complexity and integrity of an e-commerce site do not marry well with the SPA architecture.
For a non-SPA site using the React framework, javascript will throw exception if a component is not found. A SPA site will not have this issue because all components are available on the single page.
To overcome this problem, we are using the React Habitat Redux. In a nutshell, it is a layer that will help "hiding" components that are not needed for the page, thus no error for components not found.
On a typical Demandware isml page, our source code will look like this.
In react, we will render our html by using this.props.
For a non-SPA site using the React framework, javascript will throw exception if a component is not found. A SPA site will not have this issue because all components are available on the single page.
To overcome this problem, we are using the React Habitat Redux. In a nutshell, it is a layer that will help "hiding" components that are not needed for the page, thus no error for components not found.
On a typical Demandware isml page, our source code will look like this.
<isscript> var data = { "link": URLUtils.url('Cart-Show').toString(), "message": Resource.msg('global.notification.addtobag', 'locale', null) } var dataJson = JSON.stringify(data); </isscript> <div data-component="Notification_Container" data-prop-data="${dataJson}"></div>
In react, we will render our html by using this.props.
render () { return ( <div className="notification-wrapper"> <div className="message"> {this.props.data.message} </div> <div className="action"> <a href={this.props.data.link}>View Bag</a> </div> </div> ) }
The fun part of using React is to explore 3rd party (open source) components for your need. Here is a list of some of the more important ones.
- React Habitat Redux for non-SPA
- Redux Form
- react-modal
- react-image-gallery
- react-places-autocomplete
- react-collapse
IE 10, IE 11 and Safari
Some of the ES6 commands are not supported by IE 11 and Safari. We added some polyfill to our webpack and conveniently it also works for IE 10.
IE 8 and IE 9
\@_@/
IE < 8
|Orz
Firefox
In developments, we found some of the components were not rendering in Firefox. Issues were intermittent and happening about 1 out of 10 times on a page with more data.
We webpack our js in production mode and the problem has gone away since.
We webpack our js in production mode and the problem has gone away since.
Demandware Content Assets
Content assets were converted in a similar manner to other isml pages but in a better way. There are no more ad-hoc includes for random jQuery plugins. Rather, we write dedicated components to handle and support features that we want to make available to content assets.
All of these are bundled in one single bundle.js file so we can leverage client-side browser cache for performance gain.
Similar to what we previously done to isml, but we will use javascript json to hold all the properties by using React Habitat data-r-prop. Notice the difference in using <script> instead of <isscript> as it is not a Demandware script.
All of these are bundled in one single bundle.js file so we can leverage client-side browser cache for performance gain.
In our content asset, we select HTML type and simply include the <div> container.
<script> var json = { cid: 'my-content-asset-id', items: [ { title: 'Do I need to be home for my delivery?', html: '<p>Some answers to the question...</p>' }, { title: 'I have a Missing / Lost Order', html: '<p>Some more answers to another question...</p>', } ] } </script> <div data-component="Your_Container_Name" data-r-prop-fields="json"></div>
Similar to what we previously done to isml, but we will use javascript json to hold all the properties by using React Habitat data-r-prop. Notice the difference in using <script> instead of <isscript> as it is not a Demandware script.
Demandware Content Slots
Content slot in Demandware is a small widget that is time-based and user segmented. For HTML type, we convert the HTML similar to content asset. For other types, they will just work fine.
One Page Checkout
Last year, we have done a Demandware - Converting Multi-steps Checkout to One Page Checkout. To convert this to React, we simply replace our jQuery.ajax() by React component with Fetch.Google Geocoder
We use Geocoder in our store locator to display a google map for our stores. During our development, we found a race condition that sometimes the Geocoder callback is returning too late and the DOM is already re-rendered, so we wrote our own GeocoderPromise that wrapped the Google Geocoder in Promise object.
Tealium
Some of our tags were firing off on DOM ready event, which is now lazy loading in React. We had to implement our own redux-tealium to help manually triggering tags.
Thoughts
There were a few stress points during the migration.
- Learning curve of new technology
- Lack of learning materials about Demandware React integration. Almost no one has done it before if asking around in Demandware XChange.
- Using Demandware in non-SPA architecture
- Overly complicated to do something simple (eg. show/hide elements or popup modal written in React)
Was it worth the effort? Definitely.
- Code is now more readable and maintainable
- Some of our jQuery plugins are overdue for updates (which we don't need to do anymore)
- Reducing number of jQuery plugins into a handful of React components
- No more random jQuery events firing off because hidden code buried deep somewhere
- Significant overall performance gain
Thanks for this interesting post. This is something we're looking at doing as a team also. I wonder whether we could have a private discussion at some point to see whether we might a) be able to acquire your services, or b) be granted access to view some of the code in more detail. I agree, there is absolutely no one else who seems to be implementing Demandware in this way, and is surprises me that the jQuery version has been the default implementation for so long now. Let me know if you're able to discuss this more at some point.. perhaps there could be some mutual gain in our teams collaborating to some extent on the development of any new features etc?
ReplyDeleteHi Edan, I replied your email and we can talk offline :)
DeleteSunny, Thank you very much for your post! It very help us to implement opportunity for using React in our projects. But what about performance when you print large json into DOM?
ReplyDeleteFor example we use React Habitat for render of main menu. Menu has 3 levels of categories and we need set json with all categories to our menu component. This json we can see in DOM during view source of page - http://joxi.ru/BA0N1LZUBz8GXm
The more data the more the page weight and the worse the performance of render document. Could you please explain us. Is it does matter for you? And how you solve this issue?
Hi there,
DeleteI had a look at your screenshot of your HTML. If you putting raw HTML into json, then you will NOT gain any benefits by using React. Your page will become very heavy that way.
The reason why React is fast because "virtual DOM update is faster than real DOM" (ref https://hackernoon.com/virtual-dom-in-reactjs-43a3fdb1d130), so putting real DOM in json and rendering via React loses all benefits from migrating jQuery to React.
Your json should be light weight and only contain the categoryId in a nice json data structure, then your javascript need to render the HTML by only using this json data structure.
Sunny
In our case json doesn't contain any html.
Deletehttp://joxi.ru/VrwNbBwUKzQVGA We need to render much data of all categories such as name, url, orientation. React uses this data and render to html on the client side.
This comment has been removed by the author.
DeleteThen I think you are on the right track. You can render the links by using the id, so you don't need the url.
DeleteOh, yes)) Thanks)
DeleteRecently I have investigated how we can build SPA with Server Side Rendering on this example - https://github.com/ranveer5289/react-demandware-examples
I think this approach may be is very useful.
You wrote "The complexity and integrity of an e-commerce site do not marry well with the SPA architecture."
Could you please tell us why do you think so?
Because ecom system is complicated. It has a CMS component and VIP/guest user group that will change the page layout. Redesigning the entire Demandware system by using SPA will require too much effort and not much gain.
DeleteSPA is nice but non-SPA has its place, and I think it is more suitable for complicated ecom system such as Demandware. Unless you are doing WP + Shopify, then the discussion will be different.
Hello Sunny! Thank you alot for your explanation! I think this information is very useful for much people.
DeleteThis comment has been removed by the author.
ReplyDelete