Skip to content

bfischer1121/extjs2react

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ExtJS → React [Native] + Redux + Blueprint

Summary

ExtJS2React (e2r) migrates ExtJS applications to React [Native] + Redux + Blueprint by rewriting their entire codebase. Much of the work is automated while an ever-shrinking set of cases are left for manual intervention. This is an unofficial library in no way associated with Sencha. Use at your own risk.

Preparing Your Project

For speed of development and depth of implementation, it is currently assumed that the source project is ExtJS 6.x with MVVM architecture. If your project isn't there but you want to use this tool, migrate to the common starting point and then run this tool to cross the bridge to React-land: Sencha Touch → ExtJS <6.x → 6.x MVC → MVVM

Getting Started

  • Clone the repo
  • Open /config.json and modify the params accordingly
  • Run npm install in the extjs2react directory
  • Run npm start
  • ...
  • Profit!

Progress

General Modernization

  • Run output through prettier (config: prettier)
  • varlet when at root of function (config: varToLet)
  • function → arrow function when not using this or arguments (config: arrowFunctions)
  • methis when redundant (config: arrowFunctions)
  • () => { return ... }() => ... (config: arrowReturnShorthand)
  • () => { ... }() => ... (config: arrowExpressionShorthand)
  • getCount() + ' widgets'${getCount()} widgets (config: templateLiterals)

Architecture

  • ClassManager → ES6 modules
    • namespaced class names → simple names & references
    • import class dependencies
    • import alias dependencies
    • exports
    • name collision avoidance
    • resolve alternateClassName to primary
    • reactify all framework components w/ manifest
  • ExtJS classes → ES6 classes
    • extends → extend
    • statics → static
    • singleton → export instance
    • config/cachedConfig/eventedConfig → ES6 accessors & calls
    • mixins → inline js (Object.assign)
    • plugins
  • Component
    • properties → local variables
    • configs
      • definition
        • default values → merged props object
        • apply* → useMemo prop reassignment
        • update* → useEffect
      • assignment
        • declarative → JSX
        • procedural (set*)
      • reference (get*)
        • inside component → props.*
        • outside component
    • methods → inline functions
      • initialize → useEffect(..., [])
      • this.* references
        • local class members → local scope
        • inherited class members → JSX ExtJS component
      • .lookup → React reference
  • ViewController
    • properties/configs/methods → Component
    • .getView() | .view → this
    • variables referencing Component → this
    • transform lifecycle methods
  • ViewModel → state, render

Component Library

  • Ext
    • .dataview
      • .DataView
      • .List
    • .field
    • .form
      • .FieldSet
    • .layout
      • .Card
    • .Button → Button
      • text → text
      • handler | tap listener → onClick
      • pressed → active
    • .Component → div
      • html → child element
    • .Container → div
      • items → child elements
    • .Image → img
      • src → src
      • html → div + background-image + child
    • .Panel → div
    • .SegmentedButton → ButtonGroup
    • .TitleBar → div
      • title → child element
    • .Widget → div
      • disabled → disabled

Data Package

  • Stores
  • Models
    • .get('foo').foo
    • foo.set(fieldOrObject[, value])modifyRecord(foo, fieldOrObject[, value]) action
    • .getData
    • .isModel
  • Proxies

Standard Library

  • Ext
    • .Ajax
      • .getDefaultHeaders
      • .on
      • .request
    • .Array
      • .clean → _.compact
      • .clone
      • .each → Array.forEach
      • .contains → Array.includes
      • .difference → _.difference
      • .flatten → _.flattenDeep
      • .indexOf → Array.indexOf
      • .intersect → _.intersection
      • .map → Array.map
      • .pluck → _.map
      • .remove → _.pull
      • .slice
      • .sort
      • .toArray
      • .unique → _.uniq
    • .browser
      • .browser
        • .name
        • .version.version
      • .is
        • .AndroidStock2
        • .WebView
    • .Date
      • .add
      • .between
      • .clearTime
      • .diff
      • .format
      • .getShortDayName
      • .getShortMonthName
      • .parse
      • .DAY
      • .MINUTE
      • .monthNames
    • .DomQuery
      • .is
    • .Function
      • .bind → Function.bind
      • .createBuffered
      • .createSequence
      • .createThrottled
      • .interceptBefore
    • .JSON
      • .decode → JSON.parse
      • .encode → JSON.stringify
    • .Number
      • .constrain → _.clamp
      • .from
      • .toFixed → Number.toFixed
    • .Object
      • .each → _.forEach
      • .getSize → Object.keys(...).length
      • .merge
    • .String
      • .capitalize → _.upperFirst
      • .escapeRegex
      • .htmlEncode
      • .leftPad → String.padStart
      • .repeat
      • .trim → String.trim
      • .trimRegex
    • .Template → JSX
      • Member functions
      • Dynamically-determined template substrings
      • {...}
        • {(field|.):(fn|this.fn)(...)} → {(Ext.util.Format|helper).fn((field|data), ...)}
      • {[ ... ]}
        • values
        • out
        • parent
        • xindex
        • xcount
        • xkey
      • {% ... %}
      • tpl if
      • tpl elseif
      • tpl else
      • tpl for
    • .util
      • .Format
        • .date
        • .htmlEncode (see Ext.String.htmlEncode)
      • .Inflector
        • .plural
        • .pluralize
        • .singular
    • .apply → Object.assign
    • .applyIf → _.assignWith
    • .baseCSSPrefix → 'x-'
    • .bind (see Ext.Function.bind)
    • .clone → _.cloneDeep
    • .defer → setTimeout
    • .Deferred
      • self
      • .rejected
    • .emptyFn → () => {}
    • .encode (see Ext.JSON.encode)
    • .isArray → _.isArray
    • .isDate → _.isDate
    • .isDefined → !_.isUndefined
    • .isEmpty → _.isEmpty
    • .isFunction → _.isFunction
    • .isNumber → _.isFinite
    • .isNumeric → _.isFinite(+...)
    • .isObject
    • .isString → _.isString
    • .Promise
    • .toArray

Manual Tuning

ES6 Class Names

ExtJS classes are namespaced while, with modules, ES6 class names are generally not. To make the transition, e2r uses the xtype or alias of your ExtJS class as the ES6 class name. To convert lowercase xtypes to properly-cased class names, we need to distinguish words. To this end, e2r builds a word list from the class names and namespaces used within your codebase and the ExtJS framework. While this works fairly well, it requires some manual tuning:

  • Run npm run classnames in the extjs2react directory
  • Go through this list of all of your classes and add mis-capitalized words to the words array in /config.json
  • Rinse and repeat until the class names look good
  • Note: if a word has no effect, use a larger portion of the class name (longer words take precedence and yours may have been overriden)

e.g., FaceidSetup → add FaceID to config → FaceIDSetup (if no effect, add FaceIDSetup)

ES6 Modules

ExtJS dependency management is global, while ES6 dependencies are local. To make the transition, e2r builds a registry of all class names and aliases, determines each source file's requirements, and adds corresponding import and export statements.

This modularization can lead to circular dependencies. In webpack, the imported class will become undefined and, if extended, will produce the following error: Unhandled Rejection (TypeError): Super expression must either be null or a function

To resolve these issues, I recommend using a tool like circular-dependency-plugin and refactoring the original code as needed.

Dynamic Classes

Since e2r does a static code analysis, it isn't able to pick up on dynamically generated Ext.define calls. If you are dynamically creating ExtJS Classes and want to ensure these class definitions are properly imported and referenced throughout, add a comment to the file like so:

/**
 * Classes:
 * MyApp.model.Foo
 * MyApp.model.Bar
 * MyApp.user.List
 */

e2r will then add placeholder ES6 classes to the file's generated output. You can of course safely remove these as long as you export classes of the same names.

XTemplate → JSX

When compiling templates, ExtJS wraps tpl conditional statements in native with(values) blocks. This adds values to the scope chain so we can write <tpl if="age &gt; 1"> instead of <tpl if="values.age &gt; 1">.

To keep the generated JSX clean, e2r will instead auto-prefix unqualified, uncapitalized variables with values. This will change the reference if you're referring to unqualified, uncapitalized variables outside of values, requiring manual adjustment.

Need Help?

If your company is migrating an ExtJS app to React (or other framework) and would like some help, feel free to email me at my github username (looks like bfis----1121) at gmail.com