In the previous posts, we got everything set up and configured for running React Native apps in both the iOS and Android environments. Now we can start working on some code!
Go to your text editor of choice. I'm a fan of Visual Studio Code or Atom for React Native work, although most modern editors should work fine. Open up the root directory of the project. You should see folders for "android" and "ios", various config files, and "App.js" - this is where things actually happen. Open that file in your editor.
We're going to get rid of most of this file, but let's take a look around first.
At the top are a set of import calls. These specify various functions and classes we'll be referring to in this page. You'll see a lot of these imports written like:
import {View, Text} from 'react-native';
If you're not familiar with some of the new-style ECMA6 JavaScript, this might be a bit confusing. All it means is that it's getting the two specified variables out of the collection exported from that class. In this case, 'react-native' exposes all sorts of functions and classes and components, but we're only interested in the View and Text ones.
You'll be importing things from your own files soon enough!
Then there's the main body of the class. App.js is a bit different from other React Native classes we'll be working with, but the main point is that all the custom stuff is happening in the part that starts with
const App = () => {
Inside that is a return
call, which contains a bunch of stuff that looks kind of but not quite like HTML. This is JSX, and it's part of what makes React magical. The tags are components which the React Native engine turns into native iOS or Android design elements.
At the bottom of the page is a separate section for styles, which starts with
const styles = StyleSheet.create({
React Native cleverly decided to make styling into simple JavaScript objects, which coincidentally (?) makes them look a lot like regular CSS:
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: Colors.black,
},
You'll notice, though that it's almost, but not quite like CSS. Properties end with "," rather than ";", the attributes are in camelCase, and each definition has a "," at the end rather than just blank space. This is just different enough from regular CSS to throw you off quite a bit if you're jumping between regular web development and React-Native dev.
Let's go ahead and do something new. Go to the App = () => {
section, delete everything inside the return (...)
section, and replace it with:
return (
<View>
<Text>Hello World</Text>
</View>
);
Try this in your simulator. You'll notice the old content is gone and now it says "Hello World" in the far top left corner of the screen. If you're using a newer iOS device like an Xr, the text might be partially blocked by the sensors bump at the top.
Let's style this up a bit. You can assign styles directly in the tags or put them in separate Style declarations. Let's do it the direct way first:
return (
<View>
<Text style={{textAlign: 'center', paddingTop: 60, fontWeight: 'bold', fontSize: 40}}>Hello World!</Text>
</View>
);
Switch over to your iOS simulator and type CMD-R or your Android simulator and type R twice. The app should reload almost instantly with the new look. Try changing some of the values and see how the look changes. Notice the sort of awkward "{{ ... }}" multi-brackets thing: the outside brackets are a way of passing code into tags, and the inner brackets are declaring a JavaScript object for the styling information.
Let's make this cleaner by moving this content into its own area. Go down to the StyleSheet.create({ ... })
area at the bottom of the file and add this to the top:
heading: {
textAlign: 'center',
paddingTop: 60,
fontWeight: 'bold',
fontSize: 40
},
Don't forget the comma after the last bracket! Then in the tag content, replace all the details with this:
<Text style={styles.heading}>Hello World!</Text>
Reload the app and it should have not changed at all. To make sure things are actually updating, add this to the heading: { ... } description:
color: 'red',
Reload again and you should see the colour of the heading change. If you've only tried this in one of the simulators, try it in the other. It should look almost identical. Congratulations! You're now doing cross-platform native development!
Most apps end up using more than one file. For consistency it's often good to break out the styling information so it can be referenced from multiple parts of the app. Let's do that now. Set up a new file called "Styles.js" and put the following into it:
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
heading: {
textAlign: 'center',
color: 'red',
paddingTop: 60,
fontWeight: 'bold',
fontSize: 40
},
});
export default styles;
Then go back to App.js, delete the whole const styles
section (but make sure to leave the last line export default App
there), and put this in the imports section at the top:
import styles from './Styles.js';
Try reloading again. If you made any mistakes here you might end up with error messages in your simulator that just won't go away. Changes to file structure and imports often don't take with a simple reload. You'll need to stop the app and for good measure restart the react-native server you've got running in that terminal window.
To make sure your changes worked, try changing the color in the styles file to 'blue' instead of 'red'.
The magic of React is all about components, especially making and mixing in your own components. Let's get started with a fairly simple one: a basic button with a customizable label and functionality. Make a new file called "SmallButton.js" and put this into it (type it in or copy-paste - I won't tell anyone!):
import React from 'react';
import { Text, TouchableHighlight, View} from 'react-native';
import styles from './Styles.js';
export default class SmallButton extends React.Component {
constructor(props) {
super(props);
}
_pressedButton() {
this.props.onPress();
}
render() {
return (
<TouchableHighlight onPress={ () => this._pressedButton() } underlayColor="white">
<View style={styles.smallButton}>
<View style={styles.buttonFrame}>
<Text style={styles.buttonText}>{this.props.label}</Text>
</View>
</View>
</TouchableHighlight>
)
}
}
At the top, you'll see we're importing styles from our special styles file. Before we do anything else, let's add the appropriate styles to "Styles.js":
buttonFrame: {
padding: 5,
justifyContent: 'center',
alignItems: 'center'
},
buttonText: {
color: 'white',
fontWeight: 'bold',
textAlign: 'center',
fontSize: 18,
},
smallButton: {
backgroundColor: '#44f',
justifyContent: 'center',
padding: 5,
flexDirection: 'column',
margin: 5,
width: 250
},
Now if these specific styles were only happening in this button class, we might not need to clutter up our Styles file, but that's a bigger design decision you'll need to make yourself.
You'll notice in the 'smallButton' style, we have 'flexDirection' and 'justifyContents' - one of the nice things about React Native is it uses a modified version of the powerful CSS Flexbox for layout. The big thing to note is that flex direction defaults to 'row', which is the opposite of how it works on the web.
Flexbox layout is great but it can be tricky - especially since specifications in parent components might impact how things are laid out in child components. Two big things that helped me a lot with FlexBox in React Native:
React Native has a number of dev tools, some standalone, others running inside Chrome, and others even running in the simulator - they all have their strengths but I've found they're often quite fiddly and reveal more about the layers of framework used to get these effects rather than the actual stuff you're working on.
Speaking of layout, let's add these buttons to our main screen and get them calling some code:
const App = () => {
selectedStarWars = () => {
console.log("*** May the Force Be With You!");
}
selectedStarTrek = () => {
console.log("*** Live Long and Prosper!");
}
selectedMarvel = () => {
console.log("*** Excelsior!");
}
return (
<View>
<Text style={styles.heading}>PicSelect</Text>
<View style={{alignItems: 'center', paddingTop: 20}}>
<SmallButton label="Star Wars" onPress={() => this.selectedStarWars()} />
<SmallButton label="Star Trek" onPress={() => this.selectedStarTrek()}/>
<SmallButton label="Marvel" onPress={() => this.selectedMarvel()}/>
</View>
</View>
);
};
Yes it's "console" coding but this is a good way to make sure everything is connected properly. I put "***" in every console log because React Native sends out so many warnings and random errors to the console that I set most of my console outputs to filter to only show "***" and that way I only see what I need to.
Hey now we're actually building cross-platform apps with components! The actual development process in React Native is pretty fun - once you get all the setup and configuration out of the way. In the next post we'll get a bit deeper with Images, Redux, and more.