This guide will lead you through the process of integrating Airship in React.js.
We are working on React bindings that offer a better developer experience in the future.
Install airship-style
package and
1
npm i @carto/airship-style
Load the styles into your code
1
@import '@carto/airship-style/dist/airship.css';
A web component is just an HTML tag with some attributes that control its behaviour. Simply include the HTML tag, and edit its properties through attributes or through javascript as you would do with a normal HTML element.
To render a web component simply include its tag inside the REACT render function:
1
2
3
render() {
<as-infowindow src="https://thecatsite.com/styles/thecatsite/xenforo/sources/notice3.png"></as-infowindow>
}
Install airship-components
package
1
npm i @carto/airship-components
In the entry point of your application call defineCustomElements
passing window
as a parameter. Once you do this, all Airship components will be available.
1
2
3
import { defineCustomElements } from '@carto/airship-components/dist/loader';
defineCustomElements(window);
In order to manage the state, either listening to events or synchronizing the attributes we got two options.
The simplest way to link Airship and React is to manually bind attributes and events. Content attributes only accept strings as parameters so we need to use javascript to add event listeners and pass complex objects as parameter.
In the render function you can bind string
, number
and boolean
values as simple HTML attributes. Just as you do with a normal HTML tag.
In the componentDidMount you can setup event listeners and bind attributes that are arrays
or objects
. Those types cannot be passed as simple HTML attributes so you need to hook them up here.
Use componentDidUpdate to update the bind properties values.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
export default class App extends Component {
constructor(props) {
super(props);
this.categoryWidget = React.createRef();
this.state = {
heading: 'Widget Heading',
description: 'Widget description',
visibleCategories: 10,
categories: [
{ name: 'Bars & Restaurants', value: 1000, color: '#FABADA' },
{ name: 'Fashion', value: 900 },
{ name: 'Grocery', value: 800 },
{ name: 'Health', value: 400 },
{ name: 'Shopping mall', value: 250 },
{ name: 'Transportation', value: 1000 },
{ name: 'Leisure', value: 760 }
]
}
}
render() {
return (
<React.Fragment>
<button onClick={this.clearSelection.bind(this)}>Clear selection</button>
<button onClick={() => this.setState({ categories: [{ name: 'Cat 0', value: 100 }, { name: 'Cat 1', value: 90 }] })}>More Categories</button>
<button onClick={() => this.setState({ visibleCategories: this.state.visibleCategories + 1 })}>More Categories</button>
<button onClick={() => this.setState({ visibleCategories: this.state.visibleCategories - 1 })}>Less Categories</button>
<as-category-widget ref={this.categoryWidget} visible-categories={this.state.visibleCategories} heading={this.state.heading} description={this.state.description} />
</React.Fragment>
);
}
/**
* Use this method to set element properties and callbacks
*/
componentDidMount() {
this.categoryWidget.current.categories = this.state.categories;
this.categoryWidget.current.addEventListener('categoriesSelected', event => {
console.log('Categories Selected', event.detail)
});
}
/**
* Use this method to set element properties only.
*/
componentDidUpdate() {
this.categoryWidget.current.categories = this.state.categories;
}
/**
* Delegate function calls
*/
async clearSelection() {
await this.categoryWidget.current.clearSelection();
}
}
With this option we are going to create a React component that wraps the original web-component exposing a simple and react-friendly API.
See the full example here
First we create a new React Component to represent our widget, in this case a category-widget
. In the render
function we will return
the Airship category widget referenced using a ref.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default class CategoryWidget extends Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
render() {
<as-category-widget
ref={this.ref}
heading={this.props.heading}
description={this.props.description}
show-header={this.props.showHeader}
show-clear-button={this.props.showClearButton}
use-total-percentage={this.props.useTotalPercentage}
visible-categories={this.prop.visibleCategories}
/>
}
}
Then we will list all available properties using React proptypes, this tells react which attributes to accept.
1
2
3
4
5
6
7
8
9
10
11
export default class CategoryWidget extends Component {
static defaultProps = {
categories: [],
showHeader: true,
showClearButton: true,
useTotalPercentage: false,
visibleCategories: Infinity,
heading: '',
description: '',
}
}
Then we set up all the bindings in the componentDidMount
function binding every React property to the corresponding in the web component. We also associate the events, adding a native listener that connects to _onSelectedChanged
function.
onSelectedChanged is a funcion pased to the component as an attribute and will be called everytime the web components fires an categoriesSelected
event.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
export default class CategoryWidget extends Component {
componentDidMount() {
const widget = ref.current;
// Bind complex objects and event listeners
widget.categories = this.props.categories;
widget.addEventListener('categoriesSelected', this._onSelectedChanged.bind(this));
}
_onSelectedChanged(event) {
const { onSelectedChanged } = this.props;
onSelectedChanged && onSelectedChanged(event);
}
/**
* Delegate function calls
*/
async clearSelection() {
await this.ref.current.clearSelection();
}
}
With this steps we created a React wrapper over the native element that allows to pass everything as an attribute
1
2
3
4
5
6
<CategoryWidget
heading="Business Volume"
description="Description"
categories={categories}
onSelectedChanged={this.onSelectedChanged}
showClearButton={this.showClearButton}