Mobile app for a web programmer with ExponentJS

I guess everyone once in their life wants to write “a mobile app” (thanks to the seems-simple-to-create-yet-insanely-profitable app like … Flappy Bird).

There are ways to write one now, from the obvious native approach to hybrid one. There are also countless books, online courses about mobile programming. So why do I write this ?

This is for those more familiar with web programming (like me) and still wants to retain those foundations in the mobile land. Welcome to React Native and its abstract layer called Exponent.

Using Exponent, in a weekend, I wrote an app that can

  • Interact with API, for stuffs like authentication, submit data (text, photo)
  • Display data in a list with header, footer, filter and rendering indicator
  • Access native feature like phone camera, I can also resize the photo
  • Allow user to “draw” something on my app
  • Easily navigate the app
  • Easily debug and see the changes right when the code has changed
  • Easily publish the app so that users can try right away

I think you can, too

1. Mobile is different from web, but many of the foundation is still the same

You still need to let users login to your application and remembers it (so they don’t need to do the same next time)

You still need to render a list, let users search in that list, let them click on a row to view the details

You still need to make a layout (arrange buttons, forms, views on your screen). React Native made this easy with Flexbox

You still need to make a "partial" to reuse in your layout and make the code simpler (e.g. a ListView will has many row, each will be rendered using a "partial")

You still need some kind of "modal" (think Bootstrap's modal), React Native supports this nicely

You still need a way to incorporate asset into your app (thinks asset_pipeline in Rails)

Many of the call is async, this looks new at first but … not much different from the way AJAX works on web

You still need to know the life cycle of a screen, basically which event will be fired before it’s rendered (e.g. componentWillMount), which event will be fired after it’s rendered (e.g. componentDidMount), which is async which is not (most are aysnc)

You still need some kind of session/cookie to share data across your screens

You still need to log (console.log) and a place to see it (Exponent IDE)

2. And here is the new thing

Navigation is a bit tricky, think of it as a stack of screen. When you move the the next one, either add 1 more on the top of the stack (so that “back” button will bring you back) or replace the current one with the new one

In web, you can only "press" on the input form; in mobile app, you can "swipe" as well ;)

For async call, the tricky thing is how to make the latter code of yours know when the data from the async call has already “returned” and ready to use.

SetState will make the component “re-rendered” (!)

Using Exponent unfortunately means that you can’t really use the RN modules. However Exponent already supports many of that and more will come on-board soon.

3. General tips

  • Find a real assignment to work on with your mobile app (e.g. your company needs an app for something, volunteer to work on it). This will give you the pressure and determination to learn and digest new stuffs

  • Do not try to understand all the new concepts you encounter, it’s easy to get lost that way. Try simple concept (e.g. make a login page) and make something really usable out of it. It will make you feel good and feel more eager to dive deep into other stuffs.

  • Get something working by copy and paste, then try to understand what it means

  • Do not care too much about UI at first, get the flow right (e.g. let users login, view their list of task, tap on one, submit something). Getting the flow right is hard, UI is (arguably) much easier with React Native (because you know web design :) )

4. Let's start

  • Install Exponent (preferably on a Mac)

  • It's good if you have a spare Android device to test with, otherwise use Genymotion to have an Android emulator

  • Try running the stock example

  • Add a text input form. Alert what you keyed in

<TextInput
    style={{height: 40}}
    placeholder="Tài khoản"
    onChangeText={(text) => this.setState({username: text})}
/>
  • Use fetch to submit what you key in to an API. Use getsandbox.com to have your mockup API. Please note that fetch is async so the function right after it doesn't necessarily get an input returned in it

  • Try to consume the output returned by an API, use json() to convert the payload to JSON object (much more friendly with JS)

fetch('YOUR_API_ENDPOINT', {
    method: 'POST',
    headers: {
       'Accept': 'application/json',
       'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: login_body
    }).then((response) => {
       return response.json()
    }).then((responseJson) => {
    // do something
    });
  • Try to add a simple ListView with Datasource as an array of text. Then add header, footer, separator to that Listview
<ListView
    style={styles.listViewContainer}
    dataSource={this.state.dataSource}
    renderRow={ (data) => { data.props = this.props; data.router = Router; return <DeliveryTaskRow  {...data} /> } }
    renderSeparator={(sectionId, rowId) => <View key={rowId} style={styles.separator} />}
    renderHeader={() => <DeliveryTaskHeader />}
    renderFooter={() => <DeliveryTaskFooter />}
/>
  • Try to switch to Datasource with JSON object retrieve from an API
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
var dataSource: ds.cloneWithRows(responseJson),
  • Handle the event where user taps on an item in that ListView, for now just open a new Details screen. Time to try the Navigator module in Exponent. Remember, this is a stack and you can choose between add a new one or replace the existing or even reset the whole stack.

  • In the new Details screen, add a button that will "go back". This is for you to try stuffs with Navigator module

  • Up to now, you mostly show text, now try to show more stuffs in the Details screen, e.g. Photo (put them in the assets folder of your Exponent project)

<Image
    source={require('../assets/images/logo.png')}
    style={styles.welcomeImage}
/>
  • Try to access local feature, e.g. Camera. Use the built-in ImagePicker module, again this is an async module.
const output_photo = await ImagePicker.launchCameraAsync({
    allowsEditing: false
});

if (!output_photo.cancelled) { // user took a photo
    console.log('output_photo.uri = ' + output_photo.uri);
}
  • Try the React Native's ImageEditor lib to do cropping, resizing the taken photo
var ImageOffset = {
    x: 0,
    y: 0
};

var ImageCropSize = {
    width: orig_width,
    height: orig_height
};

// max 800px width
var ImageScaleSize = {
    width: 800,
    height: Math.floor(800 / orig_width * orig_height)
};

var ImageCropData = {
    offset: ImageOffset,
    size: ImageCropSize,
    displaySize: ImageScaleSize
    // resizeMode?: ?any;
};

ImageEditor.cropImage(output_photo.uri,
    ImageCropData,
    (croppedImageURI) => {
        console.log('output_photo cropped = ' + croppedImageURI);
    },
    (cropError) => console.log('output_photo cropped error = ' + cropError));
)
  • It's time to add some fancy element in your app, add ActivityIndicator element to let users know when your app is loading something
{ !this.state.loaded &&
    <ActivityIndicator
        style={[styles.centering, {height: 80}]}
            size="large"
    />
}
  • Up to know, you get the stuffs from the external server, now try to save some to your local storage on the phone. Use the built-in AsyncStorage for that, comebines this with SetState so that your component re-renders whenever you finished getting the data you want (because, async !)
AsyncStorage.getItem('user_data').then((user_data_json) => {
    // do something here
}

Subscribe to Think.Forget.Do

Sign up now to get access to the library of members-only issues.
Jamie Larson
Subscribe