© 2017 Sencha Inc. • CONFIDENTIAL • Introducing ExtReact:Adding Powerful Sencha Components to ReactApps Mark Brocato Engineering Director, Sencha @mockaroodev
© 2017 Sencha Inc. • CONFIDENTIAL • Ext JS Ext JS Components Ext JS Framework
© 2017 Sencha Inc. • CONFIDENTIAL • React: A Component Framework w/o Components Ext JS Components Ext JS Framework ExtReact React.js
© 2017 Sencha Inc. • CONFIDENTIAL • ExtReact Use All Ext JS Components in React
© 2017 Sencha Inc. • CONFIDENTIAL • Motivation for ExtReact • No complete component libraries available for React • Dependency fatigue • Form without function • Head start • Data-driven, enterprise apps
© 2017 Sencha Inc. • CONFIDENTIAL • ExtReact: 100+ Components • Grid • List • Tree • Calendar • Layouts • Form Fields • Animations • Charts • D3 Visualizations
© 2017 Sencha Inc. • CONFIDENTIAL • @extjs/reactor GitHub: extjs-reactor
© 2017 Sencha Inc. • CONFIDENTIAL • ExtReact vs Ext JS + React ExtReact • Separate license from Ext JS (annual subscription, perpetual distribution rights) • Streamlined installation (NPM) • Modern toolkit only (Ext JS 6.5) Ext JS + Reactor • Use your existing Ext JS license • Traditional Ext JS installation process • Modern + Classic toolkits
© 2017 Sencha Inc. • CONFIDENTIAL • ExtReact Grid <Grid title="Stock Prices" store={this.store}> <Column text="Company" dataIndex="name"/> <Column text="Price" dataIndex="price" formatter='usMoney'/> <Column text="Change" dataIndex="priceChange"/> <Column text="% Change" dataIndex="priceChangePct"/> <Column text="Last Updated" dataIndex="lastChange" formatter='date("m/d/Y")'/> </Grid>
© 2017 Sencha Inc. • CONFIDENTIAL • ExtReact for Ext JS Developers
© 2017 Sencha Inc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true }); Ext JS ExtReact
© 2017 Sencha Inc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped /> Ext JS ExtReact
© 2017 Sencha Inc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) } } }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped /> Ext JS ExtReact
© 2017 Sencha Inc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) } } }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} /> Ext JS ExtReact
© 2017 Sencha Inc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) }, show: { single: true, fn: function() { // load store } } } }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} /> Ext JS ExtReact
© 2017 Sencha Inc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) }, show: { single: true, fn: function() { // load store } } } }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} onShow={{ single: true, fn: () => { // load store } }} /> Ext JS ExtReact
© 2017 Sencha Inc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) }, show: { single: true, fn: function() { // load store } } }, itemTpl: ( '<div>' + '<div>{firstName} {lastName}</div>' + '<div>{title}</div>' + '</div>' ) }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} onShow={{ single: true, fn: () => { // load store } }} /> Ext JS ExtReact
© 2017 Sencha Inc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) }, show: { single: true, fn: function() { // load store } } }, itemTpl: ( '<div>' + '<div>{firstName} {lastName}</div>' + '<div>{title}</div>' + '</div>' ) }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} onShow={{ single: true, fn: () => { // load store } }} itemTpl={data => ( <div> <div>{data.firstName} {data.lastName}</div> <div>{data.title}</div> </div> )} /> Ext JS ExtReact
© 2017 Sencha Inc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { ... }, itemTpl: ( '<div>' + '<div>{firstName} {lastName}</div>' + '<div>{title}</div>' + '</div>' ) items: [{ xtype: 'toolbar', docked: 'top' items: [{ xtype: 'button', text: 'Refresh' }] }] }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} onShow={{ single: true, fn: () => { // load store } }} itemTpl={data => ( <div> <div>{data.firstName} {data.lastName}</div> <div>{data.title}</div> </div> )} /> Ext JS ExtReact
© 2017 Sencha Inc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { ... }, itemTpl: ( '<div>' + '<div>{firstName} {lastName}</div>' + '<div>{title}</div>' + '</div>' ) items: [{ xtype: 'toolbar', docked: 'top' items: [{ xtype: 'button', text: 'Refresh' }] }] }); 20 import { List, Toolbar, Button } from '@extjs/ext-react'; <List store={contacts} grouped ... itemTpl={data => ( <div> <div>{firstName} {lastName}</div> <div>{title}</div> </div> )} > <Toolbar> <Button text="Refresh" /> </Toolbar> </List> Ext JS ExtReact
© 2017 Sencha Inc. • CONFIDENTIAL • ExtReact Components are Themable • Each can be extended using SASS or Sencha Themer Triton Material iOS
© 2017 Sencha Inc. • CONFIDENTIAL • Sencha Themer 22
© 2017 Sencha Inc. • CONFIDENTIAL • FAQ • Controllers • ViewModels How much of the Ext JS framework will I use? Components Stores Models Grid, Tree, Calendar, etc… Flux (Redux, MobX, et al...)
© 2017 Sencha Inc. • CONFIDENTIAL • Components Virtual DOM DOMExtReact? Ext JS FAQ Does ExtReact use the Virtual DOM?
© 2017 Sencha Inc. • CONFIDENTIAL • FAQ Can I reuse components from pure Ext JS apps? import { reactify } from '@extjs/reactor'; const MyCustomComponent = reactify(MyPackage.MyCustomComponent); ... render() { return <MyCustomComponent ... /> }
© 2017 Sencha Inc. • CONFIDENTIAL • ExtReact: Getting Set Up
© 2017 Sencha Inc. • CONFIDENTIAL • Sign up for a trial at sencha.com npm login --registry=http://npm.sencha.com --scope=@extjs
© 2017 Sencha Inc. • CONFIDENTIAL • Use Yeoman to create new apps npm install –g yo @extjs/generator-ext-react yo @extjs/ext-react
© 2017 Sencha Inc. • CONFIDENTIAL • GitHub: sencha/extjs-reactor packages/reactor-modern-boilerplate packages/reactor-classic-boilerplate
© 2017 Sencha Inc. • CONFIDENTIAL • Adding ExtReact to an Existing React App • npm install --save @extjs/reactor @extjs/ext-react • npm install --save-dev @extjs/reactor-webpack-plugin @extjs/reactor-babel-plugin
© 2017 Sencha Inc. • CONFIDENTIAL • Adding Ext JS to an Existing React App • npm install --save @extjs/reactor • npm install --save-dev @extjs/reactor-webpack-plugin @extjs/reactor-babel-plugin • Download and unzip Ext JS from sencha.com
© 2017 Sencha Inc. • CONFIDENTIAL • Webpack import ExtReactWebpackPlugin from '@extjs/reactor-webpack-plugin’ ... plugins: [ new ExtReactWebpackPlugin({ theme: 'theme-material' }), ]
© 2017 Sencha Inc. • CONFIDENTIAL • Webpack import ExtReactWebpackPlugin from '@extjs/reactor-webpack-plugin’ ... plugins: [ new ExtReactWebpackPlugin({ theme: 'theme-material', // not needed for ExtReact sdk: '/path/to/extjs', toolkit: 'modern' packages: ['charts'] }), ]
© 2017 Sencha Inc. • CONFIDENTIAL • Babel .babelrc { "plugins": [ "@extjs/reactor-babel-plugin" ] }
© 2017 Sencha Inc. • CONFIDENTIAL • React App Launch import React from ’react'; import App from './App'; // app components import { launch } from '@extjs/reactor'; launch(<App/>); // replaces ReactDOM.render(<App/>, document.getElementById(‘root’))
© 2017 Sencha Inc. • CONFIDENTIAL • Live Demos
© 2017 Sencha Inc. • CONFIDENTIAL • Sencha Fiddle https://fiddle.sencha.com/?extreact
© 2017 Sencha Inc. • CONFIDENTIAL • Q&A
© 2017 Sencha Inc. • CONFIDENTIAL • Thank You!

Introducing ExtReact: Adding Powerful Sencha Components to React Apps

  • 1.
    © 2017 SenchaInc. • CONFIDENTIAL • Introducing ExtReact:Adding Powerful Sencha Components to ReactApps Mark Brocato Engineering Director, Sencha @mockaroodev
  • 2.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext JS Ext JS Components Ext JS Framework
  • 3.
    © 2017 SenchaInc. • CONFIDENTIAL • React: A Component Framework w/o Components Ext JS Components Ext JS Framework ExtReact React.js
  • 4.
    © 2017 SenchaInc. • CONFIDENTIAL • ExtReact Use All Ext JS Components in React
  • 5.
    © 2017 SenchaInc. • CONFIDENTIAL • Motivation for ExtReact • No complete component libraries available for React • Dependency fatigue • Form without function • Head start • Data-driven, enterprise apps
  • 6.
    © 2017 SenchaInc. • CONFIDENTIAL • ExtReact: 100+ Components • Grid • List • Tree • Calendar • Layouts • Form Fields • Animations • Charts • D3 Visualizations
  • 7.
    © 2017 SenchaInc. • CONFIDENTIAL • @extjs/reactor GitHub: extjs-reactor
  • 8.
    © 2017 SenchaInc. • CONFIDENTIAL • ExtReact vs Ext JS + React ExtReact • Separate license from Ext JS (annual subscription, perpetual distribution rights) • Streamlined installation (NPM) • Modern toolkit only (Ext JS 6.5) Ext JS + Reactor • Use your existing Ext JS license • Traditional Ext JS installation process • Modern + Classic toolkits
  • 9.
    © 2017 SenchaInc. • CONFIDENTIAL • ExtReact Grid <Grid title="Stock Prices" store={this.store}> <Column text="Company" dataIndex="name"/> <Column text="Price" dataIndex="price" formatter='usMoney'/> <Column text="Change" dataIndex="priceChange"/> <Column text="% Change" dataIndex="priceChangePct"/> <Column text="Last Updated" dataIndex="lastChange" formatter='date("m/d/Y")'/> </Grid>
  • 10.
    © 2017 SenchaInc. • CONFIDENTIAL • ExtReact for Ext JS Developers
  • 11.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true }); Ext JS ExtReact
  • 12.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped /> Ext JS ExtReact
  • 13.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) } } }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped /> Ext JS ExtReact
  • 14.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) } } }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} /> Ext JS ExtReact
  • 15.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) }, show: { single: true, fn: function() { // load store } } } }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} /> Ext JS ExtReact
  • 16.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) }, show: { single: true, fn: function() { // load store } } } }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} onShow={{ single: true, fn: () => { // load store } }} /> Ext JS ExtReact
  • 17.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) }, show: { single: true, fn: function() { // load store } } }, itemTpl: ( '<div>' + '<div>{firstName} {lastName}</div>' + '<div>{title}</div>' + '</div>' ) }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} onShow={{ single: true, fn: () => { // load store } }} /> Ext JS ExtReact
  • 18.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { itemtap: function(list, index, target, record) { console.log(`Tapped ${record.get('name')}`) }, show: { single: true, fn: function() { // load store } } }, itemTpl: ( '<div>' + '<div>{firstName} {lastName}</div>' + '<div>{title}</div>' + '</div>' ) }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} onShow={{ single: true, fn: () => { // load store } }} itemTpl={data => ( <div> <div>{data.firstName} {data.lastName}</div> <div>{data.title}</div> </div> )} /> Ext JS ExtReact
  • 19.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { ... }, itemTpl: ( '<div>' + '<div>{firstName} {lastName}</div>' + '<div>{title}</div>' + '</div>' ) items: [{ xtype: 'toolbar', docked: 'top' items: [{ xtype: 'button', text: 'Refresh' }] }] }); import { List } from '@extjs/ext-react'; <List store={contacts} grouped onItemTap={(list, index, target, record) => { console.log(`Tapped ${record.get('name')}`) }} onShow={{ single: true, fn: () => { // load store } }} itemTpl={data => ( <div> <div>{data.firstName} {data.lastName}</div> <div>{data.title}</div> </div> )} /> Ext JS ExtReact
  • 20.
    © 2017 SenchaInc. • CONFIDENTIAL • Ext.create({ xtype: 'list', store: contacts, grouped: true, listeners: { ... }, itemTpl: ( '<div>' + '<div>{firstName} {lastName}</div>' + '<div>{title}</div>' + '</div>' ) items: [{ xtype: 'toolbar', docked: 'top' items: [{ xtype: 'button', text: 'Refresh' }] }] }); 20 import { List, Toolbar, Button } from '@extjs/ext-react'; <List store={contacts} grouped ... itemTpl={data => ( <div> <div>{firstName} {lastName}</div> <div>{title}</div> </div> )} > <Toolbar> <Button text="Refresh" /> </Toolbar> </List> Ext JS ExtReact
  • 21.
    © 2017 SenchaInc. • CONFIDENTIAL • ExtReact Components are Themable • Each can be extended using SASS or Sencha Themer Triton Material iOS
  • 22.
    © 2017 SenchaInc. • CONFIDENTIAL • Sencha Themer 22
  • 23.
    © 2017 SenchaInc. • CONFIDENTIAL • FAQ • Controllers • ViewModels How much of the Ext JS framework will I use? Components Stores Models Grid, Tree, Calendar, etc… Flux (Redux, MobX, et al...)
  • 24.
    © 2017 SenchaInc. • CONFIDENTIAL • Components Virtual DOM DOMExtReact? Ext JS FAQ Does ExtReact use the Virtual DOM?
  • 25.
    © 2017 SenchaInc. • CONFIDENTIAL • FAQ Can I reuse components from pure Ext JS apps? import { reactify } from '@extjs/reactor'; const MyCustomComponent = reactify(MyPackage.MyCustomComponent); ... render() { return <MyCustomComponent ... /> }
  • 26.
    © 2017 SenchaInc. • CONFIDENTIAL • ExtReact: Getting Set Up
  • 27.
    © 2017 SenchaInc. • CONFIDENTIAL • Sign up for a trial at sencha.com npm login --registry=http://npm.sencha.com --scope=@extjs
  • 28.
    © 2017 SenchaInc. • CONFIDENTIAL • Use Yeoman to create new apps npm install –g yo @extjs/generator-ext-react yo @extjs/ext-react
  • 29.
    © 2017 SenchaInc. • CONFIDENTIAL • GitHub: sencha/extjs-reactor packages/reactor-modern-boilerplate packages/reactor-classic-boilerplate
  • 30.
    © 2017 SenchaInc. • CONFIDENTIAL • Adding ExtReact to an Existing React App • npm install --save @extjs/reactor @extjs/ext-react • npm install --save-dev @extjs/reactor-webpack-plugin @extjs/reactor-babel-plugin
  • 31.
    © 2017 SenchaInc. • CONFIDENTIAL • Adding Ext JS to an Existing React App • npm install --save @extjs/reactor • npm install --save-dev @extjs/reactor-webpack-plugin @extjs/reactor-babel-plugin • Download and unzip Ext JS from sencha.com
  • 32.
    © 2017 SenchaInc. • CONFIDENTIAL • Webpack import ExtReactWebpackPlugin from '@extjs/reactor-webpack-plugin’ ... plugins: [ new ExtReactWebpackPlugin({ theme: 'theme-material' }), ]
  • 33.
    © 2017 SenchaInc. • CONFIDENTIAL • Webpack import ExtReactWebpackPlugin from '@extjs/reactor-webpack-plugin’ ... plugins: [ new ExtReactWebpackPlugin({ theme: 'theme-material', // not needed for ExtReact sdk: '/path/to/extjs', toolkit: 'modern' packages: ['charts'] }), ]
  • 34.
    © 2017 SenchaInc. • CONFIDENTIAL • Babel .babelrc { "plugins": [ "@extjs/reactor-babel-plugin" ] }
  • 35.
    © 2017 SenchaInc. • CONFIDENTIAL • React App Launch import React from ’react'; import App from './App'; // app components import { launch } from '@extjs/reactor'; launch(<App/>); // replaces ReactDOM.render(<App/>, document.getElementById(‘root’))
  • 36.
    © 2017 SenchaInc. • CONFIDENTIAL • Live Demos
  • 37.
    © 2017 SenchaInc. • CONFIDENTIAL • Sencha Fiddle https://fiddle.sencha.com/?extreact
  • 38.
    © 2017 SenchaInc. • CONFIDENTIAL • Q&A
  • 39.
    © 2017 SenchaInc. • CONFIDENTIAL • Thank You!