/extjs-reactor

Use Ext JS components in React.

Primary LanguageJavaScriptMIT LicenseMIT

Ext JS Reactor

The @extjs/reactor package makes it easy to use Ext JS components in your React app.

Join the chat at https://gitter.im/sencha/extjs-reactor

Requirements

  • Ext JS 6.2+
  • Sencha Cmd 6.2+
  • React 15.4.0+ (peer dependency)

Installation

# Be sure to install react>=15.4.0 before
npm install --save @extjs/reactor
npm install --save-dev @extjs/reactor-webpack-plugin @extjs/reactor-babel-plugin

Getting Started

We recommend you start by cloning the boilerplate project and following the instructions there.

The boilerplate project uses Ext JS 6 with the modern toolkit. There is also a boilerplate project using the classic toolkit.

Basic Concepts

Configuration

First, install the reactor. We recommend doing this in your index.js file (your webpack entry point). This only needs to be done once in your app.

import { install } from '@extjs/reactor';
install();

If you choose to use an Ext JS component at the root of your app to handle the main layout, set the viewport option to true when installing the Ext JS renderer. This will set the height of the html, body, and react root element to 100% so that your Ext JS root component will fill the full screen. For example:

install({ viewport: true });

Hello World

The @extjs/reactor package exports a function called reactify that creates a React component for any Ext JS component class. Here's a minimal React app that renders an Ext.Panel:

import React from 'react';
import ReactDOM from 'react-dom';
import { install, reactify } from '@extjs/reactor';

// Install the Ext JS custom renderer
install();

// Create a React component to wrap Ext.Panel
const Panel = reactify('panel');

// When Ext JS loads, initialize our React app
Ext.onReady(() => {
    ReactDOM.render(
        (
            <Panel title="React Ext JS">
                Hello World!
            </Panel>
        ),
        document.getElementById('root')
    );
});

The reactify function allows you to create React components for multiple xtypes at once using array destructuring:

import { reactify } from '@extjs/reactor';
const [ Panel, Grid ] = reactify('panel', 'grid');

If you're using Babel, we recommend installing @extjs/reactor-babel-plugin, which allows you to do this instead...

import { Panel, Grid } from '@extjs/reactor/modern';

... and so our Hello World app becomes ...

import React from 'react';
import ReactDOM from 'react-dom';
import { install } from '@extjs/reactor';
import { Panel } from '@extjs/reactor/modern';

// Install the Ext JS custom renderer
install();

// When Ext JS loads, initialize our React app
Ext.onReady(() => {
    ReactDOM.render(
        (
            <Panel title="React Ext JS">
                Hello World!
            </Panel>
        ),
        document.getElementById('root')
    );
});

All of the examples below leverage the @extjs/reactor-babel-plugin to achieve this more concise syntax.

When using the classic toolkit, your import statements would look like:

import { Grid } from '@extjs/reactor/classic';

Configuring Components

React props are converted to Ext JS configs. Here's a typical use of an Ext.grid.Grid:

import React, { Component } from 'react';
import { Grid } from '@extjs/reactor/modern';

export default class MyComponent extends Component {
    render() {        
        return (
            <Grid
                plugins={[                
                    { type: 'columnresizing' }
                ]}
                columns={[
                    { text: 'Name', dataIndex: 'name' },
                    { text: 'Email', dataIndex: 'email' }
                ]}
                store={{
                    fields: ['name', 'email'],
                    data: [
                        { name: 'Tim Smith', email: 'tim101@gmail.com' },
                        { name: 'Jill Lindsey', email: 'jlindsey890@gmail.com' }
                    ]
                }}
            />
        )
    }
}

Handling Events

Any prop starting with "on" followed by a capital letter is automatically converted to an Ext JS event listener. Since Ext JS events are all lower-case, case is not preserved. You're free to use camel-case, which is common in React.

import React, { Component } from 'react';
import { Slider } from '@extjs/reactor/modern';

export default function MyComponent() {
    return (
        <Slider
            minValue={0}
            maxValue={100}
            onChange={(slider, value) => console.log(`Value set to ${value}`)}
        />
    )
}

You can also use a listeners object as is common in traditional Ext JS:

import React, { Component } from 'react';
import { Slider } from '@extjs/reactor/modern';

export default function MyComponent() {
    return (
        <Slider
            minValue={0}
            maxValue={100}
            listeners={{
                change: (slider, value) => console.log(`Value set to ${value}`)
            }}
        />
    )
}

Refs

Refs point to Ext JS component instances:

import React, { Component } from 'react';
import { Slider } from '@extjs/reactor/modern';

export default class MyComponent {
    render() {
        return (
            <Slider
                ref="slider"
                minValue={0}
                maxValue={100}
                onChange={() => this.onChange()}
            />         
        )
    }

    onChange() {
        console.log('Slider value', this.refs.slider.getValue()); // this.refs.slider is an Ext.slider.Slider
    }
}

Docked Items (Classic Toolkit)

When using the Classic Toolkit, any component with a dock prop is automatically added to (dockedItems)[http://docs.sencha.com/extjs/6.2.0/classic/Ext.panel.Panel.html#cfg-dockedItems] for your convenience.

Here is an example which docks a toolbar above a grid:

import { Grid, Panel, Toolbar, TextField } from '@extjs/reactor/classic';

function MyComponent(props) {
    return (
        <Panel layout="fit">
            <Toolbar dock="top">
                <TextField emptyText="Search..." flex={1}/>
            </Toolbar>
            <Grid>...</Grid>
        </Panel>
    )
}

Using HTML Elements and Non-Ext JS Components Inside of Ext JS Components

HTML elements and other non-Ext JS React components are wrapped in an Ext.Component instance when they appear within an Ext JS Component. This is allows Ext JS layouts to work with non-Ext JS components. For example...

<Panel layout="hbox">
    <div>left</div>
    <div>right</div>
</Panel>

... will result in two divs side-by-side. The component structure created is equivalent to:

Ext.create({
    xtype: 'panel',
    layout: 'hbox'
    items: [{
        xtype: 'component',
        html: '<div>left</div>'
    }, {
        xtype: 'component',
        html: '<div>right</div>'
    }]
});

When an Ext JS component contains only text, that text will be set as the html config of the component. For example...

<Panel>Hello World!</Panel>

... results in ...

Ext.create({
    xtype: 'panel',
    html: 'Hello World!'
});

Building

Select your toolkit, theme, and packages using [@extjs/reactor-webpack-plugin]. The plugin scans your code and only includes the classes you need in the final bundle. Here's an example:

const ExtJSReactWebpackPlugin = require('@extjs/reactor-webpack-plugin');

module.exports = {
    ...
    plugins: [
        new ExtJSReactWebpackPlugin({
            sdk: 'ext', // location of Ext JS SDK
            theme: 'theme-material',
            packages: ['charts']
        })
    ]
    ...
}

If you're using Babel, we recommend adding @extjs/reactor-babel-plugin to your .babelrc. For example:

{
  "presets": ["es2015", "react"],
  "plugins": ["@extjs/reactor-babel-plugin"]
}

Development

This is a monorepo that uses lerna. After cloning, run npm install then lerna bootstrap to install dependencies.

Packages

Contributing

Contributor License Agreement

We'd love to accept patches and new boilerplates. Before we can take them, we need you to sign this CLA.

Once we receive it, we'll be able to accept your pull requests.

Contributing a Patch

  1. Submit an issue describing your proposed change.
  2. The repo owner will respond to your issue promptly.
  3. If your proposed change is accepted, fork the repo, develop and test your code changes.
  4. Submit a pull request.