Cold Dive into React Native: A Beginner's Tutorial
Let us start by saying that React Native is a relatively new technology. It has been officially available since March 2015, been in private beta since the start of the year, and internally used at Facebook for a while before that. The saying “Rome wasn’t built in a day” generally applies to technology as well. Tools like “grunt” and platforms like “node” took years to mature. In the web world things are moving quickly, and with a huge number of frameworks, packages, and tools coming out every day, developers tend to get a little more sceptical and don’t want to jump on every hype bandwagon only to realize that they ended up in a vendor lock-in situation. We will get into what makes React Native special, why it is a technology worth getting into, and cover a few instances where it’s not all unicorns and rainbows.
Under the Hood
When talking about web technologies on mobile, available solutions usually fall in one of the following categories.
Bundling Web Applications in a Mobile Web Browser
The web application lives in a mobile browser, typically called a WebView. Without any major refactoring, a web site or web application works on the mobile device. We may need to consider mobile browser events such as tapping or listening to device orientation changes and the smaller screen for a complete user experience, but we have a working mobile version with minimal effort. Cordova/PhoneGap is the most popular option in this category. Unfortunately this option has a big downside: in some cases, applications developed using Cordova are significantly slower than native applications, especially for graphical-heavy applications. In other cases, the mobile operating system doesn’t actually provide all the features in the WebView that are available in the mobile browser. The user experience can also differ from native applications, this may happen due to the application or the platform itself. This problem may range from scrollbars not feeling the same to having a noticeable delay when tapping on elements.
Compiling to Native Technologies
A completely different solution is to create a native codebase in the end. This happens by transforming the original source code into another programming language. We trade in native performance for an abstraction layer with some uncertainties. In cases of closed-source solutions, we are not even sure what happens under the hood and with what kind of black box we are dealing with. In other cases we are not sure how much the next mobile operating system update will break our code and when fixes or updates will be available. A popular example of this category would be HaXe.
Using a JavaScript Layer
Here we use JavaScript engine of the mobile environment and execute our JavaScript there. Native controls are mapped to JavaScript objects and functions, so when we were to call a function called “fancyButtonRightHere()”, a button would appear on the screen. NativeScript or Appcelerator Titanium are well-known examples of this category.
React Native could be classified as something from the third category. For the iOS version, React Native uses “JavaScriptCore” under the hood which is the default JavaScript engine on iOS. “JavaScriptCore” is also the JavaScript engine in Apple’s Safari browsers. OS X and iOS developers can actually directly interface with it if they want to.
One big difference is that React Native runs the JavaScript code in a separate thread, so the user interface does not block and animations should be silky and smooth.
React is the Key Feature
It is worth noting that React in React Native is not put there by accident. For React Native, we need an understanding of what exactly React offers. The following concepts work the same in both React and React Native, although these code examples are tailored to be run in the browser.
Single Rendering Entry Point
When we take a look at a simple React component, the first thing we may notice is that the component has a “render” function. In fact, React throws an error if there is no render function defined inside the component.
var MyComponent = function() {
this.render = function() {
// Render something here
};
};
The special thing is that we don’t mess with DOM elements here, but we return a XML-based construct that represents what will be rendered in the DOM. This XML-based construct is called JSX.
var MyComponent = function() {
this.render = function() {
return <div className="my-component">Hello there</div>;
};
};
A special JSX transformer takes all of that XML-looking code and converts it into functions. This is what the component after the transformation will look like:
var MyComponent = function() {
this.render = function() {
return React.createElement("div", {
className: "my-component"
}, "Hello there");
};
};
The biggest advantage is that by taking a quick look at the component, we always know what it’s supposed to do. For example a “<FriendList />” component might render a number of “<Friend />” components. We can’t render our components anywhere else than inside of the “render” function, so there is never the concern that we don’t know where exactly our rendered component came from.
Unidirectional Data Flow
To build the content of a component, React provides properties or props for short. Similar to XML attributes, we pass in the props directly to a component and can then use the props inside the constructed component.
var Hello = function(props) {
this.render = function() {
return <div className="my-component">Hello {props.name}</div>;
};
};
var Greeter = function() {
this.render = function() {
return <Hello name="there" />
}
};
This leads to our components being in a tree-like structure and we are only allowed to pass data when constructing child elements.
Re-Render on Changes
In addition to props, components can also have an internal state. The most prominent example of that behavior would be a click counter that updates its value when a button is pressed. The number of clicks itself would be saved in the state.
Each of the prop and state change triggers a complete re-render of the component.
Virtual DOM
Now when everything is re-rendered when the props or state changes, how come React itself is performing that well? The magic ingredient is “Virtual DOM”. Whenever something is needed to be re-rendered, a virtual representation of the updated DOM is generated. The Virtual DOM consists of light representations of elements modeled after the component tree, making the process of generating them much more efficient than generating real DOM elements. Before applying the changes to the real DOM, checks are done to determine where exactly in the component tree the changes happened, a diff is created, and only those specific changes are applied.
Getting Started with this React Native Tutorial
There are certain prerequisites that are needed in order to develop for React Native. Since iOS is currently the only supported platform, we need Mac OS X and Xcode, at least version 6.3. io.js – a Node.js compatible platform – is needed. What helps is installing watchman through the brew package manager with brew install watchman
. While this is not necessarily needed, it helps when dealing with a lot of files inside our React Native project.
To install React Native, we simply need to install the React Native command-line application with npm install -g react-native-cli
. Calling the react-native command then helps us creat a new React Native application. Running react-native init HelloWorld
creates a folder called “HelloWorld” in which the boilerplate code can be found.
Transforming a React Application
With React being the key feature and the core principles coming from the React library, let’s take a look at what we need to transform a minimal React “Hello World” application into a React Native one.
We use some ES2015 features in this code example, specifically classes. It is completely feasible to stick with “React.createClass” or use a function form similar to the popular module pattern.
var React = require('react');
class HelloThere extends React.Component {
clickMe() {
alert('Hi!');
}
render() {
return (
<div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div>
);
}
}
React.render(<HelloThere name="Component" />, document.getElementById('content'));
Step 1: Embrace CommonJS Modules
In the first step we need to change requiring the React module to use “react-native” instead.
var React = require('react-native');
class HelloThere extends React.Component {
clickMe() {
alert('Hi!');
}
render() {
return (
<div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div>
);
}
}
React.render(<HelloThere name="Component" />, document.getElementById('content'));
What is usually a part of the tooling pipeline when developing a React web application is an integral part of React Native.
Step 2: There is no DOM
Not surprisingly, there is no DOM on mobile. Where we previously used “<div />” we need to use “<View />” and where we used “<span />” the component we need here is “<Text />”.
var React = require('react-native');
var {View} = React;
class HelloThere extends React.Component {
clickMe() {
alert('Hi!');
}
render() {
return (
<View className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</View>
);
}
}
React.render(<HelloThere name="Component" />, document.getElementById('content'));
While it’s quite convenient to put text directly in “<div />” elements, in the native world text can’t be put directly in a “<View />”. For that we need to insert a “<Text />” component.
var React = require('react-native');
var {View, Text} = React;
class HelloThere extends React.Component {
clickMe() {
alert('Hi!');
}
render() {
return (
<View className="box" onClick={this.clickMe.bind(this)}>
<Text>Hello {this.props.name}. Please click me.</Text>
</View>
);
}
}
React.render(<HelloThere name="Component" />, document.getElementById('content'));
Step 3: Inline Styles Are the Way to Go
React Native allows us to use the Flexbox modeling instead of messing around with “float” and “inline-block” that we are so familiar with in the web world. The interesting thing is that React Native does not use CSS.
var React = require('react-native');
var {View, Text, StyleSheet} = React;
class HelloThere extends React.Component {
clickMe() {
alert('Hi!');
}
render() {
return (
<View style={styles.box} onClick={this.clickMe.bind(this)}>
<Text>Hello {this.props.name}. Please click me.</Text>
</View>
);
}
}
var styles = StyleSheet.create({
box: {
borderColor: 'red',
backgroundColor: '#fff',
borderWidth: 1,
padding: 10,
width: 100,
height: 100
}
});
React.render(<HelloThere name="Component" />, document.getElementById('content'));
Using inline styles seems bewildering at first. It is similar to the transition React developers had to go through when being confronted with JSX and previously using templating engines like Handlebars or Jade.
The idea is that we don’t have stylesheets globally in the way we use CSS. We declare the stylesheets directly at component level, and so we have all the information we need to see what our component does, the layout it creates, and the styles it applies.
var React = require('react-native');
var {Text} = React;
var Headline = function(props) {
this.render = () => <Text style={headlineStyle.text}>{props.caption}</Text>;
};
var headlineStyles = StyleSheet.create({
text: {
fontSize: 32,
fontWeight: 'bold'
}
});
module.exports = Headline;
Step 4: Handling Events
The equivalent to clicking in web pages is tapping an element on the mobile device. Let’s change our code so that the “alert” pops up when we tap the element.
var React = require('react-native');
var {View, Text, StyleSheet, TouchableOpacity} = React;
class HelloThere extends React.Component {
clickMe() {
alert('Hi!');
}
render() {
return (
<TouchableOpacity onPress={this.clickMe.bind(this)}>
<View style={styles.box}>
<Text>Hello {this.props.name}. Please click me.</Text>
</View>
</TouchableOpacity>
);
}
}
var styles = StyleSheet.create({
box: {
borderColor: 'red',
backgroundColor: '#fff',
borderWidth: 1,
padding: 10,
width: 100,
height: 100
}
});
React.render(<HelloThere name="Component" />, document.getElementById('content'));
Instead of events being directly available on “<View />” components, we need to explicitly use elements that trigger events, in our case a touch event when pressing the view. There are different types of touchable components available, each of them providing a different visual feedback.
Step 5: Registering the Application
When developing with React for the browser, we just need to define a mount point, call “React.render”, and let React do its magic. In React Native, this is a little bit different.
var React = require('react-native');
var {View, Text, StyleSheet, TouchableOpacity, AppRegistry} = React;
class HelloThere extends React.Component {
clickMe() {
alert('Hi!');
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this.clickMe.bind(this)}>
<View style={styles.box}>
<Text>Hello {this.props.name}. Please click me.</Text>
</View>
</TouchableOpacity>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
box: {
borderColor: 'red',
backgroundColor: '#fff',
borderWidth: 1,
padding: 10,
width: 100,
height: 100
}
});
var MainComponent = function() {
this.render = function() {
return <HelloThere name="Component" />;
}
};
AppRegistry.registerComponent('MainComponent', function() {
return MainComponent;
});
We have to register the component for the Objective-C side of things which is done using the “AppRegistry” object. The name we give has to match with the name inside the Xcode project.
Our Hello World React Native application has significantly more lines of code than its web counterpart, but on the other side React Native takes separation of concern a bit further especially because styles are defined with the component.
As a side note, we shouldn’t rebind the “clickMe” method to the “this” context in the “render” method, especially if our React (Native) application grows to be a bit more complex. It rebinds the method on every render call which can become quite a lot. The alternative is to bind the method inside the constructor.
Running the Application
To run the application, we need to replace the contents of the “index.ios.js” file with the piece of code of our transformed application from the last step. Then we just need to open the Xcode project and press the big Run button. First a terminal will open with the React Native server, and then the simulator window will appear. The React Native server creates a bundle, which the native application will then fetch. This allows for a web development-like rapid development cycle, where changes will be reflected almost instantly in the simulator. After the terminal has popped up, our application will then show up in the simulator. Pressing CMD+D will show a development menu.
Clicking on the box will then show an alert:
For distribution, having an application that points to a local development server would not be working out for us. For this reason, we can create the bundle for usage when the React Native server isn’t running with the command react-native bundle
. In that case we need to update the “didFinishLaunchingWithOptions” method of “AppDelegate” to use the offline bundle.
This example application is also available on Github.
Working with React Native
Another thing worth mentioning is that we not only use React concepts and JavaScript for our mobile applications, but some of workflows web developers are used to are also available with React Native. When coming from web development, we are used to developer tools, inspecting elements, and live reloading.
The way React Native works is that it puts all of our JavaScript files in a bundle. This bundle is either served from a server or bundled together with the application. The first is incredibly useful for development in the Simulator, as we can enable live reloading. The developer menu React provides is by no means as mighty as the Chrome Developer Tools, but it provides a very web-like developer experience with live reloading and debugging with the Chrome (or Safari) developer tools.
Web developers are familiar with JSFiddle or JSBin, an online playground for quick web tests. There is a similar environment, RNPlay, which allows us to try out React Native in a web browser and see the result from a simulator streamed in the web browser.
Can I Use React Native?
In its current state, a few things are still in fluctuation and not up to the expectation you might have from using other frameworks. These are mainly advanced gesture recognition.
You might want to refrain from completely rewriting your mobile applications in React Native just yet. In React, one of the big advantages is that React does not impose on what workflow you want to use, as it just represents the view layer. Do you want to define your own Grunt pipeline? Or would you rather use Webpack? And will you use Backbone.js for your model needs? Or do you want go with plain JavaScript objects? Answers to all of these questions are totally up to you, because React does not put any restriction on these choices. One recommendation for React from the official website is: “since React makes no assumptions about the rest of your technology stack, it’s easy to try it out on a small feature in an existing project.”
To a certain degree, this is true for React Native as well. Mobile developers can integrate React Native as a part of their application, take advantage of the web-inspired development workflow, and choose to integrate the library on a larger scale if needs be.
React Native is not a cross-platform development framework by any means, the philosophy behind it is “Learn once, write anywhere”. While you might end up sharing some code across platforms, but there will always be code you may need to re-implement depending on the platform you are targeting.
However, one thing is certain: React Native isn’t going away. Facebook has a massive stake in it having multiple React Native powered applications in app stores. The community around React Native is growing, and with each new release there are more contributors, issues, and pull requests.[/pt_text]
[/pt_text][pt_testimonials_balloon name=”About this post” quote=”This is a shared post. Originally posted on “https://www.toptal.com/“. All rights reserved to their editors. I just shared this post on Original Author’s demand to share this post on my blog.” minheight=”50″ css_animation=”fadeIn”]