How to handle Button clicks in React Native – Part I

I’ve been building an app which lets me find movies “near me” using mocked data. In my last post, I added a progress loader to display when there’s networking latency. I have a dropdown with a list of movie names in my app, and the user can select any movie in the list. Let’s suppose the user wants to see one of these movies, and would like to see a list of closest locations where the movie is showing. We’re going to need a button so that the user can click it to get results.

You might wonder why you shouldn’t just load results when the user selects a movie name from the Picker. Well, it depends on how you feel about UX. Some apps will immediately give you results when an element is selected from a dropdown. However, just because a user selects an item doesn’t mean that is their final choice. Maybe they lifted their finger too soon, and wound up selecting something that they didn’t want. Or, maybe they’re just thinking about their different options.

My personal preference for dealing with a select is to add a confirmation button when the result of selecting an item from the Picker is a resource intensive task, like hitting a database or making a network call. You don’t want to waste resources every time a user selects an item from a dropdown.

So in this example, making a network call should only happen if the user clicks a button. Let’s add a button to our view – just below the Picker:

import { View, Picker, ActivityIndicator, Button } from 'react-native';
...
render() {
    ...
    return <View>
        <Picker...
        </Picker>
        <Button title="Find Movie Near Me"></Button>
    </View>;
    ...
}

You can’t add a Button without a title; you’ll see a warning if you do.

The button is probably the easiest control to add in React Native! Here’s what I see after I add just a couple of lines:

However, clicking the button is a different story. If you’re used to web development, you might expect an “onClick” property. Nope, it’s onPress. And you can’t just add a method like this:

...
handleClick() {
    console.log("Handled");
}
...
    <Button onPress="handleClick" title="Find Movie Near Me"></Button>
...

The app won’t complain if you do this, but if you click the button you’ll see an error: "TypeError: this.props.onPress is not a function. (In 'this.props.onPress(e);, 'this.props.onPress' is "handleClick")".

The method handleClick is actually a function of “this”, the app. So you have to reference it correctly. It doesn’t help to replace onPress="handleClick" with onPress="this.handleClick" because anything in quotes is a “literal”, treated as a string, in JSX. Instead, you have to do as follows:

...
handleClick() {
    console.log("handleClick");
}
...
    <Button onPress={this.handleClick} title="Find Movie Near Me"></Button>
...

Now the button responds to clicks, as you can see below!

If you found this interesting, click the subscribe button below! I write a new post about once a week.

How to add a progress loader in React Native

In my last post, I took a look at how network latency might affect my React Native app, and I didn’t like what I saw: a pure white screen for several seconds. How is a user supposed to “react” to that? (Pun intended!)

It was pretty clear what was happening. My app’s render method just shows a blank view if it has no state. Here’s the render method:

render() {
    if (this.state && !this.state.isLoading) {
        ...
    } else {
        return (<View></View>);
    }
}

That’s my white screen. There’s a super simple fix:

render() {
    if (this.state && !this.state.isLoading) {
        ...
    } else {
        return (<View><Text>waiting...</Text></View>);
    }
}

Now there’s text which reads “waiting…” at the top. That’s okay for a hobby app. You’re probably going to want that to look a little nicer, however. How about a progress indicator? Something that shows “we’re working on this!”. React Native comes with an ActivityIndicator which does what we want. I’m going to grab some of the code from their documentation and add it to my app. In the render method below, I’ve replaced my “waiting” text with an ActivityIndicator (and I’ve shown the import statement to remind you to add that as well).

import { View, Text, Picker, ActivityIndicator } from 'react-native';
...
render() {
    if (this.state && !this.state.isLoading) {
        ...
    } else {
        return (<View><ActivityIndicator size="large" color="#0000ff" /></View>);
    }
}

That helps some, but the progress indicator still doesn’t look very nice. It’s placed at the very top of the screen, like this:

It turns out to be pretty easy to move the ActivityIndicator to the center of the screen. You just have to add some style components to its container View, like this:

render() {
    if (this.state && !this.state.isLoading) {
        ...
    } else {
        return (<View style={[{ flex: 1, justifyContent: 'center' },
            { flexDirection: 'row', justifyContent: 'space-around', padding: 10 }]}>
            <ActivityIndicator size="large" color="#0000ff" />
        </View>);
    }
}

Here’s the result:

With very little trouble at all, I’ve now got a slick little widget to let people know that something is going on. It’s not really enough, though. What if the network request times out, and I get an error? I’ll have to delve into that more deeply later. To remind me to do this, I’ll add a TODO/FIXME comment in the code, and open an issue in my issue tracker.

If you found this interesting, click the subscribe button below! I write a new post about once a week.

How to test networking latency in React Native

In my previous post, I tested my React Native app to see what would happen if a fetch to get data from a URL resulted in an error. In my experience, URLs don’t just fail with a 404 error, however. Sometimes there’s a time delay in getting a response from a URL. This is sometimes called latency or a lag.

I wanted to see how my app would behave if the URL that it fetched had a long delay in responding. But I didn’t have a URL that would reliably take a long time to respond! How could I mimic this situation? That’s what I’ll address in this blog post.

My app does the fetch in the componentDidMount method, and then sets state to update the display, like this:

componentDidMount() {
    return fetch('https://facebook.github.io/react-native/movies.json')
        .then((response) => response.json())
        .then((responseJson) => {
            this.setState({
                isLoading: false,
                dataSource: responseJson.movies,
            });
        })
        .catch((error) => {
            // TODO FIXME replace the red screen with something informative.
            console.error(error);
        });
}

My initial attempt at adding a delay was to set a timeout around the setState call, but this resulted in an error. To simplify the code and make it easier to test, I moved the call to setState into a separate, reusable chunk of code, like this:

setMovieState(movies) {
    this.setState({
        isLoading: false,
        dataSource: movies,
    });
}

So then my componentDidMount becomes:

componentDidMount() {
    return fetch('https://facebook.github.io/react-native/movies.json')
        .then((response) => response.json())
        .then((responseJson) => {
            this.setMovieState(responseJson.movies);
        })
        .catch((error) => {
            // TODO FIXME replace the red screen with something informative.
            console.error(error);
        });
}

That looks a little cleaner, but I still need to do something to introduce a lag.

Let me write a new little piece of code which calls the method setMovieState after a specified delay:

handleMoviesResponse(movies, delay) {
    if (delay && delay > 0) {
        const timer = setTimeout(function () {
            this.setMovieState(movies);
        }.bind(this), delay);
    } else {
        this.setMovieState(movies);
    }
}

If there’s no delay, the state will be set immediately. If the method is called with a delay, then the state is not updated until after the input delay.

If you’re wondering what bind(this) is about, you may not be too familiar with JavaScript. A short answer is that the anonymous function that is passed to setTimeout uses this inside it, and that function needs to know what this is (this is my app which contains the method setMovieState.)

Finally, instead of calling setMovieState from componentDidMount, I call handleMoviesResponse, like this:

this.handleMoviesResponse(responseJson.movies, 5000);

Now that I’ve done this, I can see that when my app opens, I just see a plain white screen for 5 seconds, and then I see my movie titles dropdown (Picker) appear at the top of the page. It’s not a crash, but it seems like a bad user experience. In my next post, I’ll look at how to fix that.

blank screen when there’s network latency

If you found this interesting, click the subscribe button below! I write a new post about once a week.

What causes “SyntaxError: JSON Parse error: Unrecognized token ‘<'" in React Native?

TL;DR: In React Native, you will get “SyntaxError: JSON Parse error: Unrecognized token ‘<‘” if your URL returns a 404 error, or in general if the content is not a JSON string.

In a recent post, I showed how to display a list of movies that had been fetched from a REST API. It worked great, but I wondered what would happen to my app’s user if their device was offline, or if the REST API ever went down. To mimic this behavior, I changed the URL by adding the number 1 at the end of it, like this: “https://facebook.github.io/react-native/movies.json1”.

And here’s what I saw in the emulator:

SyntaxError: JSON Parse error: Unrecognized token ‘<‘

The red screen says “SyntaxError: JSON Parse error: Unrecognized token ‘<‘”. That may be confusing, although if you work with REST APIs for any time, you’ll soon come to recognize what it means. Meantime, how do we investigate this?

When I load up this test URL in a web browser, I see content which looks like this:

<!DOCTYPE html>
<html>
  <head>
  ...Page not found...
</html>

It’s a fancy 404 error page. That explains why response.json barfs on this; it’s not JSON. Your app expected a JSON string. It tried to parse the string into a JavaScript object, and couldn’t handle a non-JSON string. As a reminder, here’s that fetch call:

componentDidMount() {
    return fetch('https://facebook.github.io/react-native/movies.json1')
        .then((response) => response.json())
        .then((responseJson) => {
...
        })
        .catch((error) => {
            // TODO FIXME replace the red screen with something informative.
            console.error(error);
        });
}

In the longer term, I will want to replace that red screen of death with a nice error page which instructs the user what to do. I’m still developing my application, however, and as a dev, I’d rather see the stack trace for errors like this when they occur.

So to deal with this, I’ll do two things: 1) I’ll add a “TODO FIXME” note in my code. When I’m cleaning up code in the end stages of development, I know to look for these types of comments which indicate work still needs to be done. 2) I’ll open an issue in my issue tracker which will let everyone on my team know that there’s something that still has to be handled in building the application. I’ll bring this to the attention of anyone who needs to know (a project manager, perhaps). The project manager may assign a designer to build a page with some graphics or specific text to display to the user in case of this error.

If you found this interesting, click the subscribe button below! I write a new post about once a week.

How to use the Amazon AWS SDK for Textract with PHP 7.0 Asynchronously

A few days ago, I got an interesting question about my post which describes using the Amazon AWS SDK for Texttract. The question was “How can I d this with a PDF stored in S3? I know you need to use analyzeDocumentAsynch but unsure how to then get the results of the Asynch operation“.

It turns out to be pretty easy, once you’ve got the synchronous example running. The synchronous Textract example is described in that previous blog post.

Here are the code changes you need to make. Keep all the source code as before, but starting with the call to analyzeDocument, replace that and the following lines with this code:

$promise = $client->analyzeDocumentAsync($options);
$promise->then(
    // $onFulfilled
    function ($value) {
		echo 'The promise was fulfilled.';
		processResult($value);
    },
    // $onRejected
    function ($reason) {
        echo 'The promise was rejected.';
    }
);

// If debugging:
// echo print_r($result, true);
function processResult($result) {
	$blocks = $result['Blocks'];
	// Loop through all the blocks:
	foreach ($blocks as $key => $value) {
		if (isset($value['BlockType']) && $value['BlockType']) {
			$blockType = $value['BlockType'];
			if (isset($value['Text']) && $value['Text']) {
				$text = $value['Text'];
				if ($blockType == 'WORD') {
					echo "Word: ". print_r($text, true) . "\n";
				} else if ($blockType == 'LINE') {
					echo "Line: ". print_r($text, true) . "\n";
				}
			}
		}
	}
}

When you run your PHP code from the command line, you’ll notice a small wait while the asynchronous code processes, and then you’ll see the same output as before.

Here’s a link to the Guzzle Promises project to give you an idea of how to use Promises in PHP.

And here’s the full source example use of analyzeDocumentAsync.

If you found this interesting, click the subscribe button below! I write a new post about once a week.

How to add a select element in React Native

TL;DR: The React Native Picker component is the equivalent of the HTML select element.

In my last post, I used fetch to download a list of movie titles. However, I only displayed the list by using a debugging tool. My app user will want to see this list, and even better, may want to select something off the list. The commonly used web widget which does this is the HTML <select> element. In React Native, the corresponding widget is a Picker.

You can add a Picker to your render method very easily, like this:

import { Picker } from 'react-native';
...
render() {
    return <View>
        <Picker>
        </Picker>
    </View>;
}

That’s a start, but it doesn’t display my data, and it doesn’t look very interesting. We’ve got a picker with no functionality which looks like this:

Notice the little upside-down triangle in the upper right corner, which indicates the Picker has loaded.

In order to populate the Picker with data, we need to get access to the list of movies inside the render method. Fortunately, I already used setState to make the movie data persist in my app’s state (see previous post). That means I can use it in render, like this:

render() {
    let items = [];
    var length = this.state.dataSource.length;
    for (var i = 0; i < length; i++) {
        var item = this.state.dataSource[i];
        // Really, really important to not put quotes around these braces:
        items.push(<Picker.Item label={item.title} value={item.title} key={item.id} />);
    }
    return <View>
        <Picker>
        </Picker>
    </View>;
}

Uh oh, wait, as soon as I added this code, I saw the red screen of death in the emulator. The error message read “TypeError: null is not an object (evaluating ‘this.state.dataSource’). This error is located at: in HelloWorldApp (at renderApplication.js:40)…”

The problem is that I tried to reference this.state.dataSource before it had been set. The render method might be called before fetch runs. We don’t know when our fetch statement will be called, or even if it is successful. We need to add code in render which handles the case where this data is not yet available.

Here’s the new render code which skips doing anything with dataSource if it is not found:

render() {
    if (this.state && !this.state.isLoading) {
        let items = [];
        var length = this.state.dataSource.length;
        for (var i = 0; i < length; i++) {
            var item = this.state.dataSource[i];
            items.push(<Picker.Item label={item.title} value={item.title} key={item.id} />);
        }
        return <View>
            <Picker>
            </Picker>
        </View>;
    } else {
        return ( <View></View>);
    }
}

That fixes the error, but the Picker is still empty. But in anticipation of needing a list of movies, I’ve already got a list of Picker.Item objects loaded into an array using JSX (items.push(<Picker.Item....). It turns out to be very easy to add these to the Picker. Just add items in braces inside the Picker tags, like this:

<Picker>
{items}
</Picker>

As soon as this is done, like magic, the Picker reflects the changes. A dropdown appears with the text Star Wars selected by default.

Now you can click the dropdown, and try to select a different movie, but what you’ll find is that the dropdown is stuck at Star Wars – you can’t select anything else!

For a finishing touch, let’s make it possible for the user to select a different movie. The Picker documentation tells us how to do this by adding the selectedValue attribute and the onValueChange methods like this:

selectedValue={this.state.movie}
onValueChange={(itemValue, itemIndex) =>
    this.setState({ movie: itemValue })
}

After adding this I can select any movie, and that movie stays selected! Here’s my final code:

import React, { Component } from 'react';
import { View, Picker } from 'react-native';

export default class HelloWorldApp extends Component {
    componentDidMount() {
        return fetch('https://facebook.github.io/react-native/movies.json')
            .then((response) => response.json())
            .then((responseJson) => {
                console.log(responseJson);
                this.setState({
                    isLoading: false,
                    dataSource: responseJson.movies,
                }, function () {

                });

            })
            .catch((error) => {
                console.error(error);
            });
    }

    render() {
        if (this.state && !this.state.isLoading) {
            let items = [];
            var length = this.state.dataSource.length;
            for (var i = 0; i < length; i++) {
                var item = this.state.dataSource[i];
                // Really, really important to not put quotes around these braces:
                items.push(<Picker.Item label={item.title} value={item.title} key={item.id} />);
            }
            return <View>
                <Picker selectedValue={this.state.movie}
                    onValueChange={(itemValue, itemIndex) =>
                        this.setState({ movie: itemValue })
                    }>
                    {items}
                </Picker>
            </View>;
        } else {
            return (<View></View>);
        }
    }
}

If you found this interesting, click the subscribe button below! I write a new post about once a week.

Simple debugging tool in React Native

TL;DR: If you want to debug React Native code really quickly, console.log and console.warn can help.

In my previous post, I described how I ported the React Clock app to React Native. This is the code for my simple app:

import React, { Component } from 'react';
import { Text, View, Button } from 'react-native';
import Clock from './Clock';

export default class HelloWorldApp extends Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
                <Text style={{ fontWeight: 'bold', padding: 10 }}>Hello, world!</Text>
                {/* padding does not work with Button!! */}
                <Button style={{ fontWeight: 'bold', padding: 40 }} title="Click me" >Click Me!</Button>
                {/* Since padding does not work with Button, we add an empty text area */}
                <Text>{""}</Text>
                <Clock />
            </View>
        );
    }
}

For my next project, I decided to do something more realistic. I wanted to figure out how to fetch data from a REST API.

I already had the Android emulator started (see previous post). A quick look at the React-Native networking documentation told me that doing a fetch should be a piece of cake. Because I didn’t want to copy their entire sample app, but just reuse their fetch call, I copied the componentDidMount method, and pasted it into my application above the render method:

componentDidMount(){
return fetch('https://facebook.github.io/react-native/movies.json')
    .then((response) => response.json())
    .then((responseJson) => {

    this.setState({
        isLoading: false,
        dataSource: responseJson.movies,
    }, function(){

    });

    })
    .catch((error) =>{
    console.error(error);
    });
}

(The componentDidMount method may be familiar to you from React.js development.)

I didn’t see any errors when I did this, but I also couldn’t tell whether the fetch method had worked! If I had been building this app using JavaScript in a web browser, I could have quickly checked the results by adding a console.log statement to print out responseJson. I tried this, in fact, but nothing noticeable happened onscreen when I made my change. It took a little while, but I finally noticed that my statements were being logged in the terminal window that was running the Metro server (where I’d run the npm start command)! It took me a while before I noticed this because I’m not usually looking at the terminal unless I’m trying to debug a problem.

A quick search also told me that I could use console.warn to display text on the emulator’s screen. I added console.warn(responseJson); just above the setState call, and I could see that the method had succeeded, and I could also see part of the responseJson content in the YellowBox which appeared. Clicking on this YellowBox warning gave me a fullscreen view of the JSON.

Probably it’s a bad idea to display debug messages using console.warn, but if I were debugging on a device without the help of Metro server, I think console.warn would come in handy.

If you found this interesting, click the subscribe button below! I write a new post about once a week.

Invariant violation: Text strings must be rendered within a Text component

TL;DR: To display text in React Native, you must always use a Text tag. This is different from React.js, where text can be displayed in html tags like <div> or <h1>.

For my next mini React Native project, I’m porting the React Clock app to React Native. FYI I’m doing this work on Ubuntu 16.04.

I have to start up my Android emulator first. I list the available ones like this:

emulator -list-avds
Galaxy_Nexus_API_23
Galaxy_Nexus_API_28
Samsung_S4_mIni_API_17

I pick one, and start it:

emulator -avd Galaxy_Nexus_API_28

I didn’t want to have to fire up Android Studio every time I worked on a React Native project, so running the emulator from the command line is super useful. Note the emulator has nothing to do with React Native, per se, and it’s just running in a standalone mode. I can run my emulator commands from any directory; I just open a terminal and do it.

I had already gotten started with React Native. I had a folder called AwesomeProject with a directory structure like this:

...
ios/
android/
App.js

I step into this directory and run the commands npx react-native run-android followed by npm start. I see my project load into the emulator.

I’m using VS Code as my IDE for React Native development. I have no trouble making a small change or two to my App.js file, and I see these changes immediately loaded into the emulator.

Next, I add a file called Clock.js in the same directory as my App.js class.

import React from 'react';
export default class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

This is almost the same as the Clock.js code in the React.js state and lifecycle demo. I’ve removed the ReactDOM.render method because I don’t want the clock rendering immediately. Also, I’ve exported the class so it can be used by other components.

Now I add the Clock to my App. There’s just one line to import it:

import Clock from './Clock';

And a tag to add it:

<Clock/>

This is what my App.js file looks like:

import React, { Component } from 'react';
import { Text, View, Button } from 'react-native';
import Clock from './Clock';

export default class HelloWorldApp extends Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
            <Text style={{ fontWeight: 'bold', padding: 10 }}>Hello, world!</Text>
            {/* padding does not work with Button!! */}
            <Button style={{ fontWeight: 'bold', padding: 40 }} title="Click me" >Click Me!</Button>
            {/* Since padding does not work with Button, we add an empty text area */}
            <Text>{""}</Text>
            <Clock/>
            </View>
        );
    }
}

As soon as I did this, I saw an error message in my emulator:

The error message reads “Invariant violation: Text strings must be rendered within a component. This error is located at: in h1 (at Clock.js:28) in dev (at Clock.js:27)…”

Since the location of the error is very nicely displayed, I can quickly find the problem. The render method of my React.js Clock class is

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );

and the complaint is about the text “Hello, world!” within the <h1> tags. It didn’t take long before I discovered the problem in the React Native documentation for the Text component: “In React Native, we are more strict about it: you must wrap all the text nodes inside of a component. You cannot have a text node directly under a <View>.”

Let me just try using a Text tag instead of h1 and h2 tags. First I add the import statement:

import { Text } from 'react-native';

And then I swap out <h1> and <h2> for <Text>. That seems to work, but now I have a new error: “Invariant violation: View config not found for name div. Make sure to start component names with a capital letter.”

I already know the complaint is about the div tag in Clock.js. Whoops, divs do not live in native apps. I’ll just change my div tag to a Text tag and see what happens. Here’s my render code:

  render() {
    return (
        <Text>
            <Text>Hello, world!</Text>
            <Text>It is {this.state.date.toLocaleTimeString()}.</Text>
        </Text>
    );
  }

And here’s my running clock!

If you found this interesting, click the subscribe button below! I write a new post about once a week.

Unable to load script. Make sure you’re either running a Metro server…

TL;DR: run npm start in your project root to avoid this error! I’m using Ubuntu 16.04 and Android, YMMV.

In teaching myself React, I’ve been pleasantly surprised by the documentation provided by Facebook. It’s very clear, I’ve been following along easily, and I never hit a speed bump. I was expecting something similar when I started on React Native.

Unfortunately, the React Native docs are a little less polished than those for React.

I started at the appropriately named Getting Started page. I’m running Ubuntu 16.04, and I’ve done Android development, so I followed the “React Native CLI Quickstart” choose-your-own-adventure path, clicking on “Development OS: Linux” and “Target OS: Android”.

I followed the instructions to the letter, except for a few things. I already had Android Studio installed, for example, so I didn’t have to do that. However, I initialized a fresh new React Native project using their installer, just as they said, even using the same project name: npx react-native init AwesomeProject.

I got down to the section entitled “Running your React Native application”, and did as instructed:

cd AwesomeProject
npx react-native run-android

This is what I saw in the Emulator: a message with white letters on a red background saying "Unable to load script. Make sure you're either running a Metro server (run 'react-native start') or that your bundle 'index.android.bundle' is packaged correctly for release."

This was not one of those cases where I immediately knew what was wrong. Why are they talking about “Metro server”? How would I know if my bundle is packaged correctly for release??

I tried doing as the error message suggests:

~/AwesomeProject$ react-native start
react-native: command not found

No.

I went down one rabbit hole after another trying to fix the problem: First, I switched to USB debugging on an Android mobile device, because I thought the most obvious solution would be a problem with the Android Emulator (quite often, that is the problem). I got the same message there. (At this point, I started calling this page the “red screen of death”).

Finally, I got the app working in my mobile device by doing a release build:

~/AwesomeProject$ npx react-native run-android --variant=release

That worked! The app looked like this in my mobile device:

That was confidence inspiring because at least something worked. But it wasn’t useful; I wanted to be able to do hot reloading of my changes, and the install process of the release build was taking two to three minutes. No way was I going to spend 2 minutes waiting to see my changes.

Long story short, I finally figured out what was wrong (thank you, Stack Overflow).

The solution is to run npm start in the root of the project, like this:

~/AwesomeProject$ npm start

NOT react-native start, which was suggested in the error message, as I mentioned above. That was a total fail.

When I ran npm start in the command line, I saw this:

~/AwesomeProject$ npm start

> AwesomeProject@0.0.1 start /home/fullstackdev/AwesomeProject
> react-native start

┌──────────────────────────────────────────────────────────────────────────────┐
│                                                                              │
│  Running Metro Bundler on port 8081.                                         │
│                                                                              │
│  Keep Metro running while developing on any JS projects. Feel free to        │
│  close this tab and run your own Metro instance if you prefer.               │
│                                                                              │
│  https://github.com/facebook/react-native                                    │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

Looking for JS files in
   /home/fullstackdev/AwesomeProject 

Loading dependency graph, done.

 BUNDLE  [android, dev] ./index.js ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 100.0% (449/449), done.

 LOG  Running "AwesomeProject" with {"rootTag":1}

Notice the important message: “Keep Metro running while developing on any JS projects“. Doh! Thanks for telling me that.. now that it’s started, lol!

But now I’ve got my “Hello World” app running, and I can proceed working on something more interesting.

If you found this interesting, click the subscribe button below! I write a new post about once a week.

How to write SQL queries using MySQLi in PHP

MySQLi is the “MySQL Improved Extension” for PHP. You might have seen simple examples of it being used like this:

$sql = "SELECT * FROM persons";
$result = mysqli_query($conn, $sql);
echo "There are: " . mysqli_num_rows($result) . " persons in the database\n";

This particular example works. In my case, the output is “There are: 2 persons in the database”. Many queries are not so simple, however. Suppose you wanted info about a particular person named “Smith”. The following code will work (assuming $name has been set in the code somewhere above):

$name = "Smith";
...
$sql = "SELECT * FROM persons WHERE lastname = '$name'";
$result = mysqli_query($conn, $sql);
echo "There are: " . mysqli_num_rows($result) . " persons named $name\n";

The output is “There are: 1 persons named Smith”.

Because it works, it may lull you into a false sense of security; you might think that’s all there is to it. And it may work for quite a while before you run into a problem. The problem is that some names contain special characters that will break the code above. One example is the name O’Hara. This is what happens when “Smith” is replaced by “O’Hara” in the above code:

$name = "O'Hara";
...
$sql = "SELECT * FROM persons WHERE lastname = '$name'";
$result = mysqli_query($conn, $sql);
echo "There are: " . mysqli_num_rows($result) . " persons named $name\n";

When I run this code, I see an error:

PHP Fatal error:  Uncaught TypeError: mysqli_num_rows() expects parameter 1 to be mysqli_result, boolean given in /home/fullstackdev/tests/single-select-sql-apostrophe.php:29
Stack trace:...

This error might be confusing because the error is thrown from the line with mysqli_num_rows. That’s because the call to mysqli_query returned false, a sign that there was an error. Let me call mysqli_error before trying to use the $result, and print out the error, if there was one:

$name = "O'Hara";
...
$sql = "SELECT * FROM persons WHERE lastname = '$name'";
$result = mysqli_query($conn, $sql);
$error = mysqli_error($conn);
if ($error) {
        echo "$error\n";
} else {
        echo "There are: " . mysqli_num_rows($result) . " persons named $name\n";
}

Now, the output looks like:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Hara'' at line 1

This error is more helpful than the previous one, but if you’re new to SQL, it might still be confusing. The problem is that there’s an apostrophe in the name O’Hara, but the apostrophe is a special character with special meaning in SQL. It is used to signify the beginning or ending of a string. If I print out my SQL statement from the above code, it looks like this:

SELECT * FROM persons WHERE lastname = 'O'Hara'

That’s three apostrophes. There’s an unmatched apostrophe, and that means MySQL was expecting more characters; it doesn’t know what to do with this statement.

The way to handle this type of query is to “escape” the apostrophe in the SQL by putting a backslash character in front of it, like this:

SELECT * FROM persons WHERE lastname = 'O\'Hara'

That backslash notifies MySQL to treat the next character as part of the string, not as a special character delimiting a string.

Of course you don’t want to have to write special code to replace all instances of apostrophe in your string with backslash followed by apostrophe. There’s already a special PHP method which will do this for you. It’s called mysqli_real_escape_string.

Let’s use this in our code above:

$name = "O'Hara";
...
$esc_name = mysqli_real_escape_string($conn, $name);
$sql = "SELECT * FROM persons WHERE lastname = '$esc_name'";
echo "$sql\n";
$result = mysqli_query($conn, $sql);
$error = mysqli_error($conn);
if ($error) {
        echo "$error\n";
} else {
        echo "There are: " . mysqli_num_rows($result) . " persons named $name\n";
}

The output looks like this:

SELECT * FROM persons WHERE lastname = 'O\'Hara'
There are: 1 persons named O'Hara

As you can see, the SQL which I printed out has a properly “escaped” apostrophe in it. The function mysqli_real_escape_string should always be used to escape strings in cases like this.

Once you get to this point, you might think everything is hunky-dory and start writing all your code this way. However, it’s generally considered a really bad idea to write code like this, because it’s not safe against a SQL injection attack. To prevent this type of black-hat hacking on your website and database, you should always use prepared statements. It’s slightly more labor intensive, but it’s safe:

$sql = "SELECT * FROM persons WHERE lastname = ?";
$stmt = mysqli_stmt_init($conn);
mysqli_stmt_prepare($stmt, $sql);
mysqli_stmt_bind_param($stmt, 's', $name);
mysqli_stmt_bind_result($stmt, $id, $firstname, $lastname);
mysqli_stmt_execute($stmt);
$row = mysqli_stmt_fetch($stmt);
echo "Person $firstname $lastname was found\n";

The output here is Person Karen O'Hara was found.

As a final note, while MySQLi is fine to work with, IMHO, but you’ll also want to become familiar with PDO because it’s also quite popular (and different).

PS if you want to play around with the sample code above, you’ll need a MySQL database installed. Here are my dead simple database commands:

CREATE DATABASE testing;
USE testing;
CREATE TABLE persons (
    id INT AUTO_INCREMENT PRIMARY KEY,
    firstname varchar(128),
    lastname varchar(128)
);
INSERT INTO persons (firstname, lastname) VALUES ("John", "Smith");
INSERT INTO persons (firstname, lastname) VALUES ("Karen", "O'Hara");

If you found this interesting, click the subscribe button below! I write a new post about once a week.