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.
While developing a web application with Ext JS, you have no doubt defined your own custom classes and are probably wondering “is it easier or harder to do the same with React?” With React you get to use the latest ECMAScript syntax when defining your classes, something that Ext JS does not support at all. But what does that actually mean? Is there a benefit?
Class Definitions
With Ext JS you don’t get to use the class
keyword but instead use Ext.define
to define your classes.
Ext.define('MyComponnet', { extend: 'Ext.Component', xtype: 'mycomponent', html: 'Hello from Modus!' });
In this example, we’ve defined a very simple Ext JS component that extends Ext.Component
, assigns the mycomponent
alias, and provides some simple HTML. The equivalent in React would be:
import React from 'react'; const MyComponent = () => { return ( <div>Hello from Modus!</div> ); }; export default MyComponent;
This React class is called a functional class as it’s a simple function that returns some JSX. This is a fairly simple, straight forward class, but there are two points to make note of:
- The reason the
import
is there but theReact
constant is never used is because when the JSX is transpiled to JavaScript, it uses theReact
constant. If the import wasn’t there, an error will be thrown because it cannot find “React”. - The export is there so that the component can be imported and used in another file. We will be looking more at this later.
Component props
If we wanted to allow the component to have a text
config/property (known as a “prop” in the React world), we could update the component:
import React from 'react'; const MyComponent = ({ text = 'Hello from Modus!' }) => { return ( <div>{text}</div> ); }; export default MyComponent;
Note: Functional components are passed a “props” param each time the component is called. In the example above we’re expecting to receive something like <MyComponent text="Some_String" />
and if the text
prop is not passed then it’s assigned a default value of “Hello from Modus!”.
Now you can optionally define the text when you use the component in your JSX:
import React from 'react'; import MyComponent from './MyComponent'; const MyContainer = () => { return ( <div> <MyComponent /> <MyComponent text="Making the world better, one line of code at a time!" /> </div> ); }; export default MyContainer;
We have two instances of our MyComponent
. The first will use the default text of Hello from Modus!
and the second will use the text
prop passed in.
Using the class
Keyword
As we said, with React you can use the class
keyword. The reason we haven’t yet is because there are actually two different types of components with React. The functional components you have seen up to this point are simple and do not have “state”. State is a hash of values internal to a given component and will be discussed in detail in future blogs. Nor do they have lifecycle methods and can therefore be more performant. We can turn the MyComponent
into a full component by using the class
keyword and extending React.Component
:
import React, { Component } from 'react'; class MyComponent extends Component { static defaultProps = { text: 'Hello from Modus!' } render () { const { text } = this.props; return ( <div>{text}</div> ); } } export default MyComponent;
This version can be used in the same way as it was when MyContainer
used it in its JSX. It will function the same whether text
was passed or not. The difference is React’s built in state and component complexity. Since this component doesn’t use state and is very simple, a functional component is preferred. If we wanted to split the render method up into logical, separate methods in a more complex example, then using the class
keyword may be preferable.
Referencing Class Definitions
We saw how to instantiate Ext JS components in an earlier blog showing different ways to reference a class definition. When you use Ext.define
to define an Ext JS class, the name you gave it is also cached on the window object. This means that if the class used MyApp.view.Main
, you can use that class name as you would any other variable. You could type it in the browser console and it will be usable in any file (assuming the class was loaded before).
Any class designed to be used in another file / component via @import
statement must be exported from the file defining the class. An example of this was actually shown above when MyComponent
was exported like:
import React from 'react'; const MyComponent = () => { return ( <div>Hello from Modus!</div> ); }; export default MyComponent;
We could then import it:
import MyComponent from './MyComponent';
Contrary to Ext JS where classes are located on the window
object, each file that wants to use a given React component must import it. The string is the relative path from the current file to where the MyComponent
file is located. The export and import syntax can accept multiple syntaxes as it’s not just a 1-to-1 like we’ve demonstrated here. To learn more, check out MDN’s coverage of export and import.
Referencing Class Instances
Ext JS has a lot of different ways to get references to component instances. You can use ComponentQuery
along with up()
, down()
, and child()
going up and down the structure of your application. Ext JS 5’s inclusion of the MVVM pattern reintroduced the concept of references
(Ext JS 3 has a concept of refs, verison 4 got rid of it in favor of ComponentQuery
). Basically, you give an item a reference and a parent container. Then, you can look up that item via that reference. So in a ViewController
you could get a button with reference: 'mybutton'
via the lookup method:
var button = this.lookup('mybutton');
With React, it’s less common to reference to a component directly like you would with Ext JS. Instead of fetching a reference to a component and having it act upon itself, the React way is to change values on the component’s state / props. Changes to the component props or state will force the component render method to run. React’s DOM diffing engine determines whether the browser will be reflected in the browser. This is one of the more significant departures from the Ext JS world.
Updating state via user interaction
Let’s take a look at an example that shows how you could set the value of an input field by clicking on a button:
import React, { Component } from 'react'; class MyComponent extends Component { state = { text: 'Hello from Modus!' } render () { const { text } = this.state; return ( <div> <input value={text} readOnly /> <button onClick={this.onBtnClick}>Set Text</button> </div> ); } onBtnClick = () => { this.setState({ text: 'Text changed!' }); } } export default MyComponent;
Note: The way we’re defining the onBtnClick
handler here with the form onBtnClick = () => {}
automatically achieves bind()
so that this
refers to the class instance within the method. In other examples found online, you’ll also see the binding handled explicitly in the class constructor like: this.onBtnClick = this.onBtnClick.bind(this);
. Failing to bind the method to the class instance will result in the scope being the calling context rather than the class instance.
In this example, when you click on the button, it will update the component’s state and React will re-render it. When the result from the render
method is returned, it will be diffed and any changes will be written to the DOM. This differs from Ext JS as you would get a reference to the button and use its setText
method on that instance.
Remember, React will only write what was changed. It will not overwrite the entire component in the DOM like you may think by returning the full JSX in the render
method. This is an important mindset change and is one of the great things about React.
But say you wanted to take an action and avoid the component re-render altogether. As an example, let’s say we want to select text in a field. In this case, you can use React’s concept of refs:
import React, { Component } from 'react'; class MyComponent extends Component { state = { text: 'Hello from Modus!' } render () { const { text } = this.state; return ( <div> <input defaultValue={text} ref={ input => this.textInput = input } /> <button onClick={this.onBtnClick}>Select Text</button> </div> ); } onBtnClick = () => { const { textInput } = this; textInput.focus(); textInput.setSelectionRange(0, textInput.value.length); } } export default MyComponent;
In this example, the <input>
is set onto the component as the textInput
property. We can then reference it and select its value. You can read up on refs and why not to overuse it here.
Singletons
In Ext JS, some classes were created as singleton instances of that class (i.e. Ext.Date
, Ext.Ajax
). They allow you to use their properties and methods directly from the class. One way to facilitate this in React would be to export an instance of your class versus exporting the class itself. As a practical example, if we had a Utils
class defined in its own file above, we could make a singleton instance of Utils
available to other classes. We would do so by adding the following just below the class definition:
export default new Utils();
The Utils
class can then be imported in another file and its properties and methods accessed directly:
@import Utils from '../Utils.js'; // … within a separate class' logic you could then call a method from Utils: // Utils.validate(param);
Conclusion
The Ext JS class system has been around for years, from Ext.extend
to Ext.define
. JavaScript classes have only been around since the ES2015 spec. The custom class system used by Ext JS means much more code to execute when defining and instantiating a class. The native JavaScript class solution is leaner (or at the least is optimized as it’s part of the JavaScript language spec). With the basics covered for class creation and instantiation, we’ll continue this blog series by discussing how to add static props as well as getter and setter methods for class properties.
Mitchell Simoens
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…