Building a React Native App Without Tears - Part 3
Sep 17, 2019 20:02

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.

A Basic React Native File

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 Make It Our Own

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!

Breaking Things Out

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'.

Our First Custom Component

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:

  • when in doubt, add "flex: 1" to every step in the component hierarchy above and below the element that's having trouble. Remarkably, this often fixes things. Then you can make adjustments to do the layout properly.
  • the oneline documentation for React Native Flexbox is really well done and covers all the basics. The best thing about this page is it has interactive code and virtual device examples showing all the different permutations - code which you can change and then watch the device update almost instantly. Many times I've gone back to this page, found a layout close to what I wanted, adjusted the example code until it did what I wanted, and then adapted it back into my own code.
  • finally, the classic method for hack-debugging CSS headaches works here too: add brightly coloured borders or backgrounds to different elements so you can more clearly see where they're actually appearing.

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.

Other Blog Posts
Building a React Native App Without Tears - Part 3 Building a React Native App Without Tears - Part 2 Building a React Native App Without Tears (Mostly) This Is Nowhere: The Memento Edition This Is Nowhere: Aspects of Accessibility Presentations About NowHere This Is Nowhere: Head-First Into React Native This Is Nowhere: Bloomsday Halifax This Is Nowhere: Why an HTML/JavaScript Single-Page App With GPS Is A Bad Idea This Is Nowhere: GPS and Wayfinding and More UX This Is Nowhere: The Single-Button UX This Is Nowhere: Don’t Just Stand There! This Is Nowhere: Finding My Duck Finding Burgers Fast: My DIY Halifax Burger Week Site "This is Nowhere" at PodCamp Halifax 2018 The Diary Diaries: Fixing Remembary's Facebook Connection Special Leap Day Edition of "Some Weird Things About Time" What's Up With Remembary Can't get pg_dump To Work Now That Heroku Has Upgraded Postgresql to 9.4? The Best Thing I Ever Did To Promote My App If You Build It, They WON'T Come #deployaday, My Big Hairy Plan for 2015 Extracting Plain Text from an NSAttributedString My Year of "Hits" Part 2: Remembary Rolling My Year of "Hits" Part 1: Remembary Rises (and Stumbles) Handy Little Test Method to Check for Translations in Rails Apps My Suddenly Slow-Waking MacBook Air Indie App PR: Keeping Control of Your Tone A Quick Note on 'clone' in Rails 3.2 My eBook Apps 2: iOS, JavaScript, and Ruby My eBook Apps 1: Introduction Quick Tip: No Sound on Mountain Lion My Upcoming Talk at PodcampHFX 2012: My Year of "Hits" starshipsstarthere.ca: Building at the Speed of Funny Screencast Tips Remembary's Cool New Picture Support Indie App PR 2: Keeping On Top Of User Feedback Indie App PR 1: How to Handle an App Disaster Giles Bowkett Diary Project 2 Remembary Video Congratulations! Welcome to Your Nightmare! How My iPad App Remembary Took Off Why You Should Have an App in the App Store (Even If You Probably Won't Make Any Money) PodCampHFX Remembary Presentation - Part 3 How I Used MailChimp Autoresponders to Promote Remembary PodCampHFX Remembary Presentation Part 2 PodCampHFX Remembary Presentation Part 1 Why AdWords Ads Don't Work for iPad Apps Remembary is Sponsoring PodcampHFX Why Can't I Resize my Views in Interface Builder? Momento and Remembary Concerning Remembary iPad-Friendly eBooks of Gracian's Art of Worldly Wisdom Project Report: PTOS2 A Quick Note on Encryption We're all LUsers Thoughts on HAML Friday Afternoon Hack - Getting Beyond the Basics Halifax Friday Hack and Back to Basics Quote from Wil Shipley FutureRuby Make Web Not War Busy Week I: Toronto Ruby Job Fair Employment.nil - the Toronto Ruby Job Fair Code Count: Ruby on Rails vs. C#/ASP.NET A Brief Note on Twitter The Hub Halifax and Mobile Tech for Social Change Deep Thoughts on Microsoft From The Accordion Guy The Two Kinds of Defensive Programming Presentation - Fixing Careerious: From C#/.NET to Ruby on Rails Enterprise! Presenting at Ruby on Rails Project Night - May 7th New Name and New Look for Careerious/Clearfit FutureRuby and More From Unspace Health Tips for Programmers This tables meme won't die Careerious - Ruby and Rails vs. C#/.NET Yeah I Use Tables For Layout, So Sue Me The Different Kinds of Done Giles Bowkett's RubyFringe presentation OfficeTime: Great Time-Tracking App for OS X Back With A New Look Non-DRY Feed torontorb Keeping Your Sanity With The Command Design Pattern shindigital Is All Grown Up! (according to the spambots) Startup Stars? I'm so bored! The Magic Words for RMagick Jennifer from Operations You see? Naming is HARD Business Software as Process Documentation Deployment note: 'execve failed' Steve Jobs on Market Research Why Canada Is Better for Entrepreneurs "Program first and blog second" Toronto Tech Collage The MacBook Air Is A Roadster RubyFringe! Quote of the Week: Steve Yegge Starting Up: Cards Great design tool: browsershots.org Starting Up: The Logo Quotes Of The Day: Hedge Fund Interview TSOT Ruby / Rails Presentation Night - Part 1 Moneyworks: Accounting Software for Canadians on OS X Starting Up: The Name Nice logo, but why is your site so bland? Welcome to shindigital.com