This is part of the Ext JS to React blog series. You can review the code from this article on the Ext JS to React Git repo.
In the previous blog in this series, we looked at how to define classes with React. In this blog post, we’ll expand upon that by looking at adding static members to a class. Also, Ext JS has a special config system. It automatically generates getter and setter methods for config options defined in the class config
block. We’ll explore how to accomplish the same end goal within React.
Note: While not a requirement for React, this article’s code examples assume you’re starting with a starter app generated by create-react-app.
Statics
A static member can be a property or method and is used to retrieve information or handle something without creating an instance of the class the statics are defined on. These members belong to the class they are defined on. In fact, JavaScript itself has static members on its classes like Date’s parse method. You use this method like:
var milliseconds = Date.parse('Jan 1, 2017');
This static method will take in the date string, parse it, and return the number of milliseconds since the epoch. The static method is a handy mechanism to have versus having to create an instance and execute a function:
var milliseconds = new Date('Jan 1, 2017').getTime();
Ext JS statics
Ext JS classes can also define static members while using Ext.define
, but you must nest these members within the statics
object:
Ext.define('MyError', { statics : { codes : { NO_NETWORK : 1, SAVE_ERROR : 2, NO_PERMISSION : 3 }, fromAjax : function (response, doThrow) { var error; var hasErrors; // hasErrors = true; // uncomment to spoof a 404; if (hasErrors || response.status === 404) { error = new MyError({ code : MyError.codes.NO_NETWORK, text : response.statusText }); } if (doThrow) { throw error; } return error; } } }); Ext.define('Foo', { doSomething : function () { return Ext.Ajax .request({ url : '/foo' }) .then(this.handleAjax) .catch(function (response) { MyError.fromAjax(response, true); }); } }); var instance = new Foo(); instance .doSomething() .catch(console.log);
In this example, we have a MyError
class that holds a static codes
object. It allows easy code lookup and a method that can handle common errors from an ajax request.
React statics defined
We can create a simple example with React classes. The MyError class would be defined as:
export default class MyError { static codes = { NO_NETWORK: 1, SAVE_ERROR: 2, NO_PERMISSION: 3 } constructor(config) { Object.assign(this, config); } static fromAjax(response, doThrow) { let error; let hasErrors; // hasErrors = true; // uncomment to spoof a 404 if (hasErrors || response.status === 404) { error = new MyError({ code: MyError.codes.NO_NETWORK, text: response.statusText }); } if (doThrow) { throw error; } return error; } }
Using React statics
The Foo class has an asynchronous action called during its construction:
import React, { Component } from 'react'; import MyError from './MyError'; class Foo extends Component { constructor(props) { super(props); fetch('/foo') .then(response => { MyError.fromAjax(response, true); }) .catch(a => { // handle error here }); } render() { return ( <div>Hello there</div> ); } } export default Foo;
With ES2015, to define a member as static, you define it like you would a regular member. You need to prefix the member with the static
keyword. Note that the static keyword is a more modern inclusion in the JavaScript language and doesn’t have support across all browsers / browser versions. A transpiler such as Babel aims to rescue you from legacy JavaScript dependency and will handle the normalization of code using newer conventions such as the static
keyword within legacy browsers. If your application is created using Create React App much of the transpiling is taken care of for you behind the scenes (including the handling of static
).
Creating an instance of Foo in our application is very straightforward:
import React from 'react'; import Foo from './Foo'; export default() => <Foo />;
Note: For those not starting with create-react-app you can make use of the class properties Babel plugin.
Configs
As it evolved, Ext JS grew to include a way to define configurations for classes and manage the getting and setting of those configs. When you define a config within a config: {}
block, Ext JS will create getter and setter functions for you automatically. The getter function simply returns the underlying property that is used to store the value of the config. The setter sets the property but also calls an applier and updater function to allow the class to transform and/or react to the config being set.
Ext JS configs
An example of this would look like:
Ext.define('MyApp.view.Main', { config : { foo : 'bar' }, constructor: function (config) { this.initConfig(config); }, applyFoo : function (foo) { return Ext.Array.from(foo); }, updateFoo : function (foo, oldFoo) { // react to foo change } });
Configurations go within the config
object, which will kick off the getter and setter method generation upon class instantiation. The instance will then have getFoo
and setFoo
methods that should be used instead of direct property access. The applyFoo
method will be called by the generated setFoo
method to transform the value as needed. In this case we attempt to normalize the value passed to setFoo
into an array. The updateFoo
may be used to react to the foo
value change in order to do things like update the DOM or another instance property. You would use the getter and setter like:
var instance = new MyApp.view.Main(), value = instance.getFoo(); // would be [ 'bar' ] instance.setFoo('foobar'); value = instance.getFoo(); // would be [ 'foobar' ]
React configs
React doesn’t require a config system like Ext JS does. We will just use native JavaScript. We can actually use the properties without the need to execute a method in our code. For example, this class defines a getter and setter:
export default class Foo { constructor(config) { Object.assign(this, config); } get foo() { return this._foo; } set foo(foo = []) { if (foo && !Array.isArray(foo)) { foo = [foo]; } this._foo = foo; // react to foo change } }
As you can see, this native JavaScript version is pretty similar to the Ext JS equivalent except we don’t have to define the config
object. In order for this setter to have the same applier / updater hooks as Ext JS you can include one of two things. You can call separate before (applier) and after (updater) methods from within the setter. Or, you can integrate the applier / updater logic within the setter code itself. You would use the getter and setter a bit differently by accessing and setting the property directly instead of executing methods:
const instance = new Foo({ foo : 'bar' }); let value = instance.foo; console.log('value', value); // value [ 'bar' ] instance.foo = 'foobar'; value = instance.foo; console.log('value', value); // value [ 'foobar' ]
Using properties directly instead of executing methods feels a bit more intuitive and natural. Also, since it’s all native JavaScript, performance will be better. If the same was done using Ext JS, generated getter and setter methods would be introduced on class instantiation. If you liked the separate applier/updater methods that are executed, there is nothing stopping you from executing these in your setter.
Conclusion
Ext JS has one of the most powerful JavaScript class systems, but breaking it down begins to show you can accomplish the same thing with JavaScript alone. Statics and the config system is something that is utilized heavily within Ext JS and can be used in applications. Thankfully, the same thing can be done when transitioning to React. Stay tuned for the next blog post where we’ll take a look at how React enables the sharing of methods and properties between different classes!
Seth Lemmons
Related Posts
-
Ext JS to React: Migration to Open Source
Worried about Migrating from Ext JS? Modus has the Answers Idera’s acquisition of Sencha has…
-
Ext JS to React: FAQ
This is part of the Ext JS to React blog series. React is Facebook's breakout…