How to Create a Google AutoComplete form in React Native
Published: 2020-09-11 03:00:04

Hi there, custom location search bars are usually part of app designs and if you want to build a custom location search bar in your app, this article is for you🌺.

There's an already existing, amazing package for [google-autocomplete](https://www.npmjs.com/package/react-native-google-places-autocomplete) in react native, but if you want to build a more custom interface, follow this guide and see how you can do it.

Here's what we'll create at the end of this article : 
![completed project](https://i.ibb.co/1mY0n84/completed-project.gif)

I'm happy to let you know that Google already has an API for getting autocomplete data (predictions) for:

* Addresses
* Places
* Zip Codes


According to Google:
> The Place Autocomplete service is a web service that returns place predictions in response to an HTTP request. The service can be used to provide autocomplete functionality for text-based geographic searches, by returning places such as businesses, addresses and points of interest as a user types.

The Place Autocomplete API is usually in the following form:

`https://maps.googleapis.com/maps/api/place/autocomplete/json?key=API_KEY&input=SEARCH_KEYWORD`.

You'll need an API key to use this API. You can go ahead and get one [here]([https://developers.google.com/places/web-service/get-api-key](https://developers.google.com/places/web-service/get-api-key)) . You can also go through the google place API [documentation]([https://developers.google.com/places/web-service/autocomplete](https://developers.google.com/places/web-service/autocomplete)) for more information.

So what we'll be doing can be simplified into 4 steps
1. Create the input form (TextInput)
2. Search Google places for the keyword 
3. Get the top 5 closest predictions
4. Display the predictions in a list (FlatList). 

![google autocomplete in react native](https://i.ibb.co/Cm7BSkp/Drawing.png)

Are you ready?😄

###  Create a new React Native project🔥
`react-native init autocompleteExample`

### Navigate into the project and it running with
```
cd autocompleteExample

react-native run-ios 
		or  
react-native run-android
```

### Step 1: Create the Input form
```javascript
    import React, {Component} from 'react';
    import {View,TextInput,StyleSheet,SafeAreaView} from 'react-native';
    
    export default class App extends Component {
      render() {
        return (
          <SafeAreaView style={styles.container}>
            <TextInput
              placeholder="Search for an address"
              placeholderTextColor="#000"
              style={styles.searchBox}
              onChangeText={(text) => this.searchLocation(text)}
              value={this.state.searchKeyword}
            />
          </SafeAreaView>
        );
      }
    }
    const styles = StyleSheet.create({
      searchBox: {
        width: 340,
        height: 50,
        fontSize: 18,
        borderRadius: 8,
        borderColor: '#aaa',
        color: '#000',
        backgroundColor: '#fff',
        borderWidth: 1.5,
        paddingLeft: 15,
      },
      container: {
        flex: 1,
        backgroundColor: 'lightblue',
        alignItems: 'center',
      },
    });
```
Our app should look  like this now
	 ![text input added](https://i.ibb.co/kGsxD3Y/Screenshot-2020-06-10-at-4-39-19-PM.png)

### Step 2: Search Google places for the keyword 
Now let's write the code to search for predictions every time the user types in something.

We'll connect the search function to the  `onChangeText` property of the `TextInput`, so it runs the `searchLocation` function anytime the user types in something. 

In this function, we'll use Google's place autocomplete API to search any keyword the user types in and get an array of predictions.

If the request is successful, we run the function:
```
 this.setState({
   searchResults: response.data.predictions,
   isShowingResults: true,
 });
```
 This function passes the data we've received to `this.state.searchResults` and it also sets the boolean `this.state.isShowingResults` to `true`  so that the FlatList  (which we would be creating very soon) can be visible since we now have data.


```javascript
  searchLocation = async (text) => {
    this.setState({searchKeyword: text});
    axios
      .request({
        method: 'post',
        url: `https://maps.googleapis.com/maps/api/place/autocomplete/json?key=${API_KEY}&input=${this.state.searchKeyword}`,
      })
      .then((response) => {
        console.log(response.data);
        this.setState({
          searchResults: response.data.predictions,
          isShowingResults: true,
        });
      })
      .catch((e) => {
        console.log(e.response);
      });
  };

  ```
Firing this API request returns a response that contains an array labelled  `predictions` which contains the list of the top 5 predictions for the keyword searched in JSON (JavaScript Object Notation) format.

For example, if I type the word "**Chi**", I would get a response that looks something like this:

```javascript
{
    "predictions": [
        {
            "description": "Chicago, IL, USA",
            "id": "53eeb015d61056c54245a909c79862532fc953ad",
            "place_id": "ChIJ7cv00DwsDogRAMDACa2m4K8",
        },
        {
            "description": "China",
            "id": "10bfe2265f06933baf8c2f1e35bf3bf132d74377",
            "place_id": "ChIJwULG5WSOUDERbzafNHyqHZU",
        },
        {
            "description": "Chihuahua, Mexico",
            "id": "26f4ee675aa61f33dd0ffc296b582baf2e08fa3e",
            "place_id": "ChIJM0BIXZ1E6oYRex3dBqen8bc",
        },
        {
            "description": "Chile",
            "id": "c33bb522167791f4bf271ef9151c1d13a1dd1092",
            "place_id": "ChIJL68lBEHFYpYRHbkCERPhBQU",
        }
    ],
    "status": "OK"
}
```
***note🍀***: I've omitted some part of the response object in this JSON sample to keep it short and simple, there's usually a lot more information in the response objects.

The   **JSON response**  mainly contains two root elements:

-   `status` : this contains the [metadata](https://developers.google.com/places/web-service/autocomplete#place_autocomplete_status_codes) of the request.  
-   `predictions`: this contains an array of places, with information about the place. See Google's [Place Autocomplete Results](https://developers.google.com/places/web-service/autocomplete#place_autocomplete_results)  for detailed information about these results.

The Places API usually returns up to 5 results.

Of particular interest within the results are the  `place_id`  elements, which can be used to request more specific [details]([https://developers.google.com/places/web-service/details#PlaceDetailsRequests](https://developers.google.com/places/web-service/details#PlaceDetailsRequests)) about the place via a separate query. 

 We'll save the predictions array to state (or redux) `this.state.searchResults`, then display the list of predictions from there.

See where this is going? 😉.

### Step 5: Displaying the autocomplete data
So now we have gotten the autocomplete/predictions data in a structure similar to the JSON sample above, which we have stored in `this.state.searchResults`.

 To display the autocomplete data, we'll use a `FlatList` that would be placed below the `TextInput` to display the predictions.
 
![Google location autocomplete FlatList breakdown](https://i.ibb.co/0nCr8XG/Search-Result-UI-Break-Down.png)

Let's do that quickly: 
```javascript

{
  this.state.isShowingResults && (
      <FlatList
        data={this.state.searchResults}
        renderItem={({item, index}) => {
          return (
            <TouchableOpacity
              style={styles.resultItem}
              onPress={() => this.setState({searchKeyword: item.description})}>
              <Text>{item.description}</Text>
            </TouchableOpacity>
          );
        }}
        keyExtractor={(item) => item.id}
		style={styles.searchResultsContainer}  
    />
  );
}
  ```
  
 In this part where we show the list, we'll utilize a concept known as **Short-circuit evaluation** in javascript.
```
{
	this.state.isShowingResults && (
	<View>
	//Our List
	</View>
	);
}
```
In simple terms, it means the rest of the code (which displays the View that contains the list) will only be executed if the first argument (`this.state.isShowingResults `) is true. 

For example, the Text "**Hello there**🐣" will only be rendered if the value of `this.state.isShowingText` is `true`
```
{
	this.state.isShowingText &&
	<Text> Hello there🐣 </Text> 
}
```

Hope you're understanding😊, and if you're not, kindly leave a comment.

This short-circuiting is done to make sure we only show the list when we've got data from the autocomplete API request. 

So our app should look like this now.

![displacing a view](https://i.ibb.co/qx0KT0t/view-displaced.gif)

🎉 Hooray, our autocomplete now works.

But we still have a little problem. What if there's some other component below the `TextInput`?

Notice how the FlatList displaces the pink dummy View below it?

To solve this, we do two things:
1.  wrap the `TextInput` and `FlatList` with a View and give that view a high `zIndex` of say 10 so it shows in front of anything below it.
2. Make the FlatList absolute so it shows on top of any view or element below it.

Like this:
```javascript
...
<View style={styles.autocompleteContainer}>
	<TextInput .../>
	<FlatList ... />
</View>
 
//...styles
autocompleteContainer: {
	zIndex: 10,
},
searchResultsContainer: {
	width: 340,
	height: 200,
	backgroundColor: '#fff',
	position: 'absolute',
	top: 50,
},
```
And that's it 🎉Our search perfectly works now

![completed project](https://i.ibb.co/1mY0n84/completed-project.gif)

Here's our complete code:
```javascript
import React, {Component} from 'react';
import {
  View,
  Text,
  FlatList,
  TouchableOpacity,
  TextInput,
  StyleSheet,
  SafeAreaView,
} from 'react-native';
import axios from 'axios';

const API_KEY = 'Your-API-Key';
export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      searchKeyword: '',
      searchResults: [],
      isShowingResults: false,
    };
  }

  searchLocation = async (text) => {
    this.setState({searchKeyword: text});
    axios
      .request({
        method: 'post',
        url: `https://maps.googleapis.com/maps/api/place/autocomplete/json?key=${API_KEY}&input=${this.state.searchKeyword}`,
      })
      .then((response) => {
        console.log(response.data);
        this.setState({
          searchResults: response.data.predictions,
          isShowingResults: true,
        });
      })
      .catch((e) => {
        console.log(e.response);
      });
  };

  render() {
    return (
      <SafeAreaView style={styles.container}>
        <View style={styles.autocompleteContainer}>
          <TextInput
            placeholder="Search for an address"
            returnKeyType="search"
            style={styles.searchBox}
            placeholderTextColor="#000"
            onChangeText={(text) => this.searchLocation(text)}
            value={this.state.searchKeyword}
          />
          {this.state.isShowingResults && (
            <FlatList
              data={this.state.searchResults}
              renderItem={({item, index}) => {
                return (
                  <TouchableOpacity
                    style={styles.resultItem}
                    onPress={() =>
                      this.setState({
                        searchKeyword: item.description,
                        isShowingResults: false,
                      })
                    }>
                    <Text>{item.description}</Text>
                  </TouchableOpacity>
                );
              }}
              keyExtractor={(item) => item.id}
              style={styles.searchResultsContainer}
            />
          )}
        </View>
        <View style={styles.dummmy} />
      </SafeAreaView>
    );
  }
}

const styles = StyleSheet.create({
  autocompleteContainer: {
    zIndex: 1,
  },
  searchResultsContainer: {
    width: 340,
    height: 200,
    backgroundColor: '#fff',
    position: 'absolute',
    top: 50,
  },
  dummmy: {
    width: 600,
    height: 200,
    backgroundColor: 'hotpink',
    marginTop: 20,
  },
  resultItem: {
    width: '100%',
    justifyContent: 'center',
    height: 40,
    borderBottomColor: '#ccc',
    borderBottomWidth: 1,
    paddingLeft: 15,
  },
  searchBox: {
    width: 340,
    height: 50,
    fontSize: 18,
    borderRadius: 8,
    borderColor: '#aaa',
    color: '#000',
    backgroundColor: '#fff',
    borderWidth: 1.5,
    paddingLeft: 15,
  },
  container: {
    flex: 1,
    backgroundColor: 'lightblue',
    alignItems: 'center',
  },
});
```

Here's the link to the complete example repo
https://github.com/mosoakinyemi/autocomplete-example

Good luck!