## Adding React to the current Knockout frontend # 'cause it needs to be better #### by Jisse Reitsma (Yireo)
# Jisse Reitsma - Founder of Yireo - Trainer of Magento 2 developers - Creator of MageTestFest & Reacticon - Organizer of usergroups, hackathons - Member of ExtDN (Magento Ext Dev Network) - Magento Master 2017 & 2018 Mover - For some reason, called **"Knockout" Jisse**
## Welcome to # Knockout Gate
## Here is a slide on # PWA
## It's a hype, so here's another one # PWA
## Back to # Knockout Jisse
# Doppelganger ### an apparition or double of a living person.
# Who is ## the evil Dale Cooper?
# Who is ## the evil Ash from Evil Dead?
# Who is ## the evil politician?
# Which one is ## the evil UiComponent?
## Adding React to the current Knockout frontend # Replacing the native minicart with a React component
# Summary of Knockout - Initial release in 2010 - Back then, a better alternative to Angular - Now superseeded by other solutions - React - Vue - Polymer - Web Components
# Summary of UiComponents - Combination of technologies - XML Layout + Blocks + PHTML templating - RequireJS + mixins - KnockoutJS + HTML templates - Custom Magento logic - Overly complex
# Minicart UiComponent - XML layout, Block class and PHTML template generate JSON blob - `x-magento-init` uses JSON blob to initialize UiComponent `minicart.js` - UiComponent `minicart.js` creates KO ViewModel definition - KO ViewModel is instantiated and bound to `scope: minicart_content` - `customerData` cart details are received from AJAX or localStorage - UiComponent calls upon child ViewModels + templates
# What if we could migrate?
# ... to React - Modern JS framework - Simpler to work with, once you get the hang - JS, HTML, CSS - all combined in 1 single component - Used by upcoming PWA techs like Magento PWA Studio
GitHub repo
https://github.com/yireo-training/Yireo_ReactMinicart
# Module structure (1 of 2) ``` registration.php etc/module.xml view/frontend/layout/default.xml view/frontend/templates/minicart.phtml view/frontend/requirejs-config.js view/frontend/web/css/source/_module.less ```
# Module structure (2 of 2) ``` view/frontend/web/js/container.js view/frontend/web/js/react.js view/frontend/web/js/react-dom.js view/frontend/package.json view/frontend/gulpfile.js view/frontend/source/Minicart.js view/frontend/web/js/compiled/Minicart.js ```
# Migration method for Minicart - Copy real-life HTML from Element Inspector - Remove all KO parts - Remove all KO comments (containerless bindings) - Remove all element bindings (`data-bind=`)
# Minicart HTML Old HTML: ```html
My Cart
1
``` New HTML: ```html
My Cart
1
```
# Migration method for Minicart - Copy real-life HTML from Element Inspector - Remove all KO parts - Remove all KO comments (containerless bindings) - Remove all element bindings (`data-bind=`) - Start copying HTML to React component (and subcomponents) - How cool: PhpStorm converts HTML to JSX - Make logic dynamic - `this.props.cart` is populated from localStorage - Create dropdown with `
` HTML element
# requirejs-config.js ```js var config = { map: { '*': { react: 'Yireo_ReactMinicart/js/react', reactDom: 'Yireo_ReactMinicart/js/react-dom', reactMinicart: 'Yireo_ReactMinicart/js/container', reactMinicartComponent: 'Yireo_ReactMinicart/js/compiled/Minicart', reactCustomerData: 'Yireo_ReactMinicart/js/customerData' } }, shim: { reactMinicartComponent: ['react', 'reactDom'] } }; ```
# PHTML template ```php
= __('My Cart') ?>
```
# container.js ```js define([ 'react', 'reactDom', 'reactMinicartComponent', 'Magento_Customer/js/customer-data' ], function(React, ReactDOM, MinicartComponent, customerData) { 'use strict'; return function(config, element) { var reactElement = React.createElement(MinicartComponent.default); ReactDOM.render(reactElement, element); customerData.get('cart').subscribe(function() { var reactElement = React.createElement(MinicartComponent.default); ReactDOM.render(reactElement, element); }); }; }); ```
# React component source ```js import CustomerData from './CustomerData'; import React from 'react'; import Cart from "./Minicart/Cart"; import EmptyCart from "./Minicart/EmptyCart"; class Minicart extends React.Component { render() { var cart = CustomerData.getCartFromLocalStorage(); return (
...
); } } export default Minicart; ```
# React components ```js Minicart Minicart/Cart Minicart/Cart/Actions Minicart/Cart/Product Minicart/Cart/ProductDetails Minicart/Cart/Subtotal Minicart/EmptyCart ```
# Source compilation gulpfile.js: ```js gulp.task("build", function () { return gulp .src(jsFiles.source) .pipe(babel()) .pipe(gulp.dest("web/js/compiled/")); }); ``` .babelrc: ```json { "presets": ["react", "es2015"], "plugins": ["transform-es2015-modules-amd"] } ```
# Kat in het bakkie
# Minicart React component - Gulp to compile ES6+React code into plain ES5 files - KO listener to re-render React component when customerData.get('cart') changes - Dropdown based on React click-handler and state, not complex UiComponent - Simple CustomerData object to copy data from localStorage
# Current limitations - No support for text translations (yet) - No way to send state back to KO ViewModels (?)
# Lessons learned - Knockout sucks, React sucks less - You can gradually add React components to existing frontend - Once Magento 2.3 is out, start playing with GraphQL too
# Slides ## slides.yireo.com/yireo/react_to_ko