React Native Tutorial: Building Android Apps with JavaScript
In this React Native tutorial you’ll learn how to build native apps based on the hugely popular React JavaScript library, with a focus on Android. By Christine Abernathy.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
React Native Tutorial: Building Android Apps with JavaScript
40 mins
- Getting Started
- Node and Java Development Kit
- React Native CLI
- Android Development Environment
- Create the Starter App
- React Native Basics
- Using JSX
- Adding Navigation
- Building out the Search Page
- Styling with Flexbox
- Handling Assets
- Adding Component State
- Initiating a Search
- Performing an API Request
- Displaying the Results
- A Touch of Style
- Where To Go From Here?
Adding Component State
A React component can manage its internal state through an object called, you guessed it, state
. Whenever a component’s state changes, render()
is called.
Within SearchPage.js, add the following code just before render()
:
constructor(props) {
super(props);
this.state = {
searchString: 'london'
};
}
Your component now has a state
variable, with searchString
set to an initial value of london
.
Within render()
, change TextInput
to the following:
<TextInput
underlineColorAndroid={'transparent'}
style={styles.searchInput}
value={this.state.searchString}
placeholder='Search via name or postcode'/>
This sets the TextInput
value property — that is, the text displayed to the user — to the current value of the searchString
state variable. This takes care of setting the initial state, but what happens when the user edits this text?
The first step is to create a method that acts as an event handler. Within the SearchPage
class add the following method below the constructor
:
_onSearchTextChanged = (event) => {
console.log('_onSearchTextChanged');
this.setState({ searchString: event.nativeEvent.text });
console.log('Current: '+this.state.searchString+', Next: '+event.nativeEvent.text);
};
This defines a function using the =>
syntax. This is an arrow function, another recent addition to the JavaScript language that provides a succinct syntax for creating anonymous functions.
The function takes the value from the native browser event’s text
property and uses it to update the component’s state. It also adds some logging code that will make sense shortly.
Note: JavaScript classes do not have access modifiers, so they have no concept of private
. As a result you often see developers prefixing methods with an underscore to indicate that they should be considered private.
Note: JavaScript classes do not have access modifiers, so they have no concept of private
. As a result you often see developers prefixing methods with an underscore to indicate that they should be considered private.
To wire up this method so it gets called when the text changes, return to the TextInput
field within the render
method and add an onChange
property so the tag looks like the following:
<TextInput
underlineColorAndroid={'transparent'}
style={styles.searchInput}
value={this.state.searchString}
onChange={this._onSearchTextChanged}
placeholder='Search via name or postcode'/>
Whenever the user changes the text, you invoke the function supplied to onChange
; in this case, it’s _onSearchTextChanged
.
There’s one final step before you refresh your app again: add the following logging statement to the top of render()
, just before return
:
console.log('SearchPage.render');
Save your changes and return to your emulator. You should see the text input’s initial value set to london:
Run the following in terminal to view the debug logs:
react-native log-android
In the emulator, edit the input text. You should see something like this:
08-01 18:09:02.720 5444 8028 I ReactNativeJS: SearchPage.render
08-01 18:09:33.453 5444 8028 I ReactNativeJS: _onSearchTextChanged
08-01 18:09:33.453 5444 8028 I ReactNativeJS: Current: london, Next: londona
08-01 18:09:33.454 5444 8028 I ReactNativeJS: SearchPage.render
Looking at the console logs, the order of the logging statement seems a little odd:
- This is the initial call to
render()
to set up the view. - You invoke
_onSearchTextChanged()
when the text changes. - You call
this.setState()
to schedule an update to the component state to reflect the new input text. This triggers another render. - You log the current and the next search text values.
A React component state change triggers a UI update. This de-couples the rendering logic from state changes affecting the UI. Most other UI frameworks put the onus on you to update the UI based on state changes. Alternatively, the updates are done through an implicit link between the state and UI, for example by using Android’s Data Binding Library.
At this point you’ve probably spotted a fundamental flaw in this concept. Yes, that’s right — performance!
Surely you can’t just throw away your entire UI and re-build it every time something changes? This is where React gets really smart.
Each time the UI renders itself, it takes the view tree returned by your render methods, and reconciles — or diffs — it with the current Android UI view. The output of this reconciliation process is a simple list of updates that React needs to apply to the current view. That means only the things that have actually changed will re-render!
You can wrap your head around all that later; you still have some work to do in the app.
Initiating a Search
First, remove the logging code you just added above, you’ll no longer need it.
In order to implement the search functionality you need to handle the Go button press, create a suitable API request, and provide a visual indication that a query is in progress.
Within SearchPage.js, update the initial state within the constructor:
this.state = {
searchString: 'london',
isLoading: false,
};
The new isLoading
property will keep track of whether a query is in progress.
Add the following logic to the start of render
:
const spinner = this.state.isLoading ?
<ActivityIndicator size='large'/> : null;
This is a ternary if
statement that optionally adds an activity indicator, depending on the component’s isLoading
state. Because the entire component is rendered each time, you are free to mix JSX and JavaScript logic.
Within the JSX that defines the search UI in return
, add the following line below the Image
to place the spinner:
{spinner}
Next, add the following methods to the SearchPage
class:
_executeQuery = (query) => {
console.log(query);
this.setState({ isLoading: true });
};
_onSearchPressed = () => {
const query = urlForQueryAndPage('place_name', this.state.searchString, 1);
this._executeQuery(query);
};
_executeQuery()
will eventually run the query, but for now it simply logs a message to the console and sets isLoading
appropriately so the UI can show the new state.
_onSearchPressed()
configures and initiates the search query. This should kick off when the Go button is pressed.
To accomplish that, go back to the render
method and replace the onPress
prop for the Go Button
as follows:
onPress={this._onSearchPressed}
Finally, add the following utility function just above the SearchPage
class declaration:
function urlForQueryAndPage(key, value, pageNumber) {
const data = {
country: 'uk',
pretty: '1',
encoding: 'json',
listing_type: 'buy',
action: 'search_listings',
page: pageNumber,
};
data[key] = value;
const querystring = Object.keys(data)
.map(key => key + '=' + encodeURIComponent(data[key]))
.join('&');
return 'https://api.nestoria.co.uk/api?' + querystring;
}
urlForQueryAndPage
doesn’t depend on SearchPage
, so it’s implemented as a free function rather than a method. It first creates the query string based on the parameters in data
. Then it transforms the data into name=value
pairs separated by ampersands. Finally, it calls the Nestoria API to return the property listings.
Save your changes, head back to the emulator and press Go. You’ll see the activity indicator spin:
In terminal, the debug logs should show something like this:
08-01 18:14:45.110 5444 8308 I ReactNativeJS: https://api.nestoria.co.uk/api?country=uk&pretty=1&encoding=json&listing_type=buy&action=search_listings&page=1&place_name=london
Copy and paste that URL into your browser to see the result. You’ll see a massive JSON object. Don’t worry — you don’t need to understand that! You’ll add code to parse that now.