





















































React is an open-sourced JavaScript library made by Facebook for building UI applications. The project has a strong emphasis on the component-based approach and utilizes the full power of JavaScript for constructing all elements. The React Native project was introduced during the first React conference in January 2015. It allows you to build native mobile applications using the same concepts from React. In this post I am going to explain the main building blocks of React Native through the example of an iOS demo application. I assume that you have previous experience in writing web applications with React.
Please go through getting started section on the React Native website if you would like to build an application on your machine.
When all of the necessary tools are installed, let's initialize the new React application with the following command:
react-native init LastFmTopArtists
After the command fetches the code and the dependencies, you can open the new project (LastFmTopArtists/LastFmTopArtists.xcodeproj) in Xcode. Then you can build and run the app with cmd+R. You will see a similar screen on the iOS simulator:
You can make changes in index.ios.js, then press cmd+R and see instant changes in the simulator.
In this post I will show you how to build a list of popular artists using the Last.fm api. We will display them with help of ListView component and redirect on the artist page using WebView.
Let's start with adding a new screen into our application. For now it will contain dump text. Create file ArtistListScreen with the following code:
var React = require('react-native');
var {
ListView,
StyleSheet,
Text,
View,
} = React;
class ArtistListScreen extendsReact.Component {
render() {
return (
<View style={styles.container}>
<Text>Artist list would be here</Text>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
marginTop: 64
}
})
module.exports = ArtistListScreen;
Here are some things to note:
From index.ios.js we call ArtistListScreen using NavigatorIOS component:
var React = require('react-native');
var ArtistListScreen = require('./ArtistListScreen');
var {
AppRegistry,
NavigatorIOS,
StyleSheet
} = React;
var LastFmArtists = React.createClass({
render: function() {
return (
<NavigatorIOS
style={styles.container}
initialRoute={{
title: "last.fm Top Artists",
component: ArtistListScreen
}}
/>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
});
Switch to iOS Simulator, refresh with cmd+R and you will see:
After we have got the empty screen, let's render some mock data in a ListView component. This component has a number of performance improvements such as rendering of only visible elements and removing which are off screen. The new version of ArtistListScreen looks like the following:
class ArtistListScreen extendsReact.Component {
constructor(props) {
super(props)
this.state = {
isLoading: false,
dataSource: newListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
}
}
componentDidMount() {
this.loadArtists();
}
loadArtists() {
this.setState({
dataSource: this.getDataSource([{name: 'Muse'}, {name: 'Radiohead'}])
})
}
getDataSource(artists: Array<any>): ListView.DataSource {
returnthis.state.dataSource.cloneWithRows(artists);
}
renderRow(artist) {
return (
<Text>{artist.name}</Text>
);
}
render() {
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
automaticallyAdjustContentInsets={false}
/>
</View>
);
}
}
Side notes:
The end result of the changes:
The Last.fm web api is free to use but you will need a personal api token in order to access it. At first it is necessary to join Last.fm and then get an API account.
I assume you have successfully set up the API account. Let's call a real web service using fetch API:
const API_KEY='put token here';
const API_URL = 'http://ws.audioscrobbler.com/2.0/?method=geo.gettopartists&country=ukraine&format=json&limit=40';
const REQUEST_URL = API_URL + '&api_key=' + API_KEY;
loadArtists() {
this.setState({
isLoading: true
});
fetch(REQUEST_URL)
.then((response) => response.json())
.catch((error) => {
console.error(error);
})
.then((responseData) => {
this.setState({
isLoading: false,
dataSource: this.getDataSource(responseData.topartists.artist)
})
})
.done();
}
After a refresh, the iOS simulator should display:
Since we have real data, it is time to add artist's images and rank them on the display. Let's move artist cell display logic into separate component ArtistCell:
'use strict';
var React = require('react-native');
var {
Image,
View,
Text,
TouchableHighlight,
StyleSheet
} = React;
class ArtistCell extendsReact.Component {
render() {
return (
<View>
<View style={styles.container}>
<Image
source={{uri: this.props.artist.image[2]["#text"]}}
style={styles.artistImage}
/>
<View style={styles.rightContainer}>
<Text style={styles.rank}>## {this.props.artist["@attr"].rank}</Text>
<Text style={styles.name}>{this.props.artist.name}</Text>
</View>
</View>
<View style={styles.separator}/>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
padding: 5
},
artistImage: {
height: 84,
width: 126,
marginRight: 10
},
rightContainer: {
flex: 1
},
name: {
textAlign: 'center',
fontSize: 14,
color: '#999999'
},
rank: {
textAlign: 'center',
marginBottom: 2,
fontWeight: '500',
fontSize: 16
},
separator: {
height: 1,
backgroundColor: '#E3E3E3',
flex: 1
}
})
module.exports = ArtistCell;
Changes in ArtistListScreen:
// declare new component
var ArtistCell = require('./ArtistCell');
// use it in renderRow method:
renderRow(artist) {
return (
<ArtistCell
artist={artist}
/>
);
}
Press cmd+R in iOS Simulator:
The last piece of the application would be to open a web page by clicking in ListView. Declare new component WebView:
'use strict';
var React = require('react-native');
var {
View,
WebView,
StyleSheet
} = React;
class Web extendsReact.Component {
render() {
return (
<View style={styles.container}>
<WebView url={this.props.url}/>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F6F6EF',
flexDirection: 'column',
},
});
Web.propTypes = {
url: React.PropTypes.string.isRequired
};
module.exports = Web;
Then by using TouchableHighlight we will call onOpenPage from ArtistCell:
class ArtistCell extendsReact.Component {
render() {
return (
<View>
<TouchableHighlight
onPress={this.props.onOpenPage}
underlayColor='transparent'>
<View style={styles.container}>
<Image
source={{uri: this.props.artist.image[2]["#text"]}}
style={styles.artistImage}
/>
<View style={styles.rightContainer}>
<Text style={styles.rank}>## {this.props.artist["@attr"].rank}</Text>
<Text style={styles.name}>{this.props.artist.name}</Text>
</View>
</View>
</TouchableHighlight>
<View style={styles.separator}/>
</View>
);
}
}
Finally open web page from ArtistListScreen component:
// declare new component
var WebView = require('WebView');
class ArtistListScreen extendsReact.Component {
// will be called on touch from ArtistCell
openPage(url) {
this.props.navigator.push({
title: 'Web View',
component: WebView,
passProps: {url}
});
}
renderRow(artist) {
return (
<ArtistCell
artist={artist}
// specify artist's url on render
onOpenPage={this.openPage.bind(this, artist.url)}
/>
);
}
}
Now a touch on any cell in ListView will load a web page for selected artist:
You can explore source code of the app on Github repo. For me it was a real fun to play with React Native. I found debugging in Chrome and error stack messages extremely easy to work with. By using React's component-based approach you can build complex UI without much effort. I highly recommend to explore this technology for rapid prototyping and maybe for your next awesome project.
Want more JavaScript? Visit our dedicated page here.
Eugene Safronov is a software engineer with a proven record of delivering high quality software. He has an extensive experience building successful teams and adjusting development processes to the project’s needs. His primary focuses are Web (.NET, node.js stacks) and cross-platform mobile development (native and hybrid). He can be found on Twitter @sejoker.