Add a Search Bar Using Hooks and FlatList in React Native
Tags: reactnative, javascript, mobile
Published: 2020-09-02 16:27:17

---
title: Add a Search Bar Using Hooks and FlatList in React Native
published: true
description: FlatList is a component of the React Native API that allows fetching and displaying large arrays of data. In this tutorial, we use it to add a search bar.
tags: reactnative, javascript, mobile
canonical_url: https://blog.jscrambler.com/add-a-search-bar-using-hooks-and-flatlist-in-react-native/
cover_image: https://blog.jscrambler.com/content/images/2020/08/jscrambler-blog-hooks-flatlist-in-react-native.jpg
---

A common use case to display data when developing mobile apps with React Native is in the form of a list.

Two common ways exist in React Native to create lists: `ScrollView` and `FlatList`. Each of these components from the framework's API has its strength.

In this tutorial, let us explore different props provided by `FlatList` to fetch data, display data, and add a search bar.

## Prerequisites

To follow this tutorial, please make sure you are familiarized with JavaScript/ES6 and meet the following requirements on your local dev environment.

- [Node.js](https://nodejs.org/) version >= `12.x.x` installed
- Have access to one package manager such as npm or yarn
- [expo-cli](https://github.com/expo/expo-cli) version installed or use npx

The example in the following tutorial is based on [Expo](https://expo.io/) SDK `38`.

## Getting Started

To create a new Expo based project, open up a terminal window and run the following commands in the sequence they are described.

Make sure to install `lodash.filter` after the project directory is generated. We are going to use the package to filter the data later when adding a search from the list functionality.

```bash
npx expo init [Project Name]

# choose a template when prompted
# this example is using the 'blank' template

# after the project directory has been generated

cd [Project Name]

# install dependency
yarn add lodash.filter
```

Once the new project is created and you have navigated inside it, run `yarn start`. Whether you use a simulator or a real device, you are going to get the following result.

<center><img src="https://blog.jscrambler.com/content/images/2020/08/jscrambler-blog-hooks-and-flatlist-react-native-1.png" alt="Hooks and Flatlist in React Native"></center>

## Using a FlatList Component

React Native's `FlatList` is an efficient way to create scrolling lists that consist of a large amount of data without degrading the overall performance. It is optimized for large arrays of data because it renders only a set of items that are displayed on the screen. When scrolling through a list of data, the internal state is not preserved - as compared to ScrollView, which renders all the data immediately after mounting the component. This means that all the data in ScrollView is mounted on the device's memory and can lead to degraded performances when a large amount of data is being rendered.

Passing an array of data to the `FlatList` is how you can display the list of data. Let's see how this works. For example, open `App.js` and before the function component, add the following array of data.

```js
const data = [
  { id: '1', title: 'First item' },
  { id: '2', title: 'Second item' },
  { id: '3', title: 'Third item' },
  { id: '4', title: 'Fourth item' }
];
```

Next, import the `FlatList` in the `App.js` file.

```js
import { StyleSheet, Text, View, FlatList } from 'react-native';
```

The `FlatList` is going to use three primary props that are required to display a list of data:

- `data`: an array of data that is used to create a list. The array consists of multiple objects as elements.
- `keyExtractor`: tells the `FlatList` to use a unique identifier or an `id` for the individual elements of the array.
- `renderItem`: a function that takes an individual element from the array of data and renders it on the UI.

Then, modify the `App` component to return this list of `data`.

```js
export default function App() {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Basic FlatList Example</Text>
      <FlatList
        data={data}
        keyExtractor={item => item.id}
        renderItem={({ item }) => (
          <View style={styles.listItem}>
            <Text style={styles.listItemText}>{item.title}</Text>
          </View>
        )}
      />
    </View>
  );
}
```

Add the following `styles` object.

```js
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f8f8f8',
    alignItems: 'center'
  },
  text: {
    fontSize: 20,
    color: '#101010',
    marginTop: 60,
    fontWeight: '700'
  },
  listItem: {
    marginTop: 10,
    padding: 20,
    alignItems: 'center',
    backgroundColor: '#fff',
    width: '100%'
  },
  listItemText: {
    fontSize: 18
  }
});
```

Now, go back to the simulator, and you are going to see that all objects inside the `data` array are now displayed in the form of a list. Using `FlatList` takes minimal effort to display organized data.

<center><img src="https://blog.jscrambler.com/content/images/2020/08/jscrambler-blog-hooks-and-flatlist-react-native-2.png" alt="Hooks and Flatlist in React Native"></center>

## Fetching data from an API in FlatList

FlatList doesn't care about how the mobile app is fetching data. In the previous section, we did learn about how to mock an array of data and consume it as a list. In this section, let's fetch the data from a remote API resource and follow the same pattern (as in the previous section) to display the data.

**Side Note:** For a remote API resource, I am going to use the [Random User Placeholder API](https://randomuser.me/api/?seed=1&page=1&results=20).

Start by importing all the necessary components that we are going to use in this section. Update the following import statements as shown below:

```js
import React, { useState, useEffect } from 'react';
import {
  StyleSheet,
  Text,
  View,
  FlatList,
  ActivityIndicator,
  Image
} from 'react-native';
```

Next, define the URL of the API endpoint to fetch the data from as a constant.

```js
const API_ENDPOINT = `https://randomuser.me/api/?seed=1&page=1&results=20``;
```

The HTTP request to the API endpoint is going to fetch the first 20 results for now.

Define three state variables inside the `App` component using the React Hook `useState`. The `isLoading` state variable is going to have a boolean value of `false` by default. Its purpose is to display a loading indicator when the data is being fetched from the API endpoint.

The `data` variable is going to have an empty array by default. Using this state variable, the `FlatList` is populated to display a list of data.

The last state variable, `error`, is going to have a default value of `null`. It is only going to update when there is an error fetching the data.

```js
export default function App() {
  const [isLoading, setIsLoading] = useState(false);
  const [data, setData] = useState([]);
  const [error, setError] = useState(null);

  // ...
}
```

Next, using the React Hook `useEffect` and the `fetch` API from JavaScript, let's fetch the data from the `API_ENDPOINT`. Add the following after you have defined the state variables inside the `App` component.

The `loading` variable is set to `true` once the `useEffect` instantiates. The boolean value of this variable is only set to `false` either when the fetching of data is complete or when there is an error. The `setData` below is updating the `data` variable with an array of data.

```js
export default function App() {
  // state variables defined

  useEffect(() => {
    setIsLoading(true);

    fetch(API_ENDPOINT)
      .then(response => response.json())
      .then(results => {
        setData(results);
        setIsLoading(false);
      })
      .catch(err => {
        setIsLoading(false);
        setError(err);
      });
  }, []);
  // ...
}
```

Then add two `if` conditions, each returning a JSX for two different scenarios. First, when the data is being fetched, a loading indicator is shown. Second, when there is an error, an error message is displayed.

```js
export default function App() {
  // state variables defined

  // fetch data using useEffect

  if (isLoading) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <ActivityIndicator size="large" color="#5500dc" />
      </View>
    );
  }

  if (error) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text style={{ fontSize: 18}}>
          Error fetching data... Check your network connection!
        </Text>
      </View>
    );
  }
  // ...
}
```

Then, update the `FlatList` to display the user avatar and the name of the user fetched from the API endpoint.

```js
export default function App() {
  // ...

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Favorite Contacts</Text>
      <FlatList
        data={data}
        keyExtractor={item => item.first}
        renderItem={({ item }) => (
          <View style={styles.listItem}>
            <Image
              source={{ uri: item.picture.thumbnail }}
              style={styles.coverImage}
            />
            <View style={styles.metaInfo}>
              <Text style={styles.title}>{`${item.name.first} ${
                item.name.last
              }`}</Text>
            </View>
          </View>
        )}
      />
    </View>
  );
}
```

Don't forget to update the `styles` object as well.

```js
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f8f8f8',
    alignItems: 'center'
  },
  text: {
    fontSize: 20,
    color: '#101010',
    marginTop: 60,
    fontWeight: '700'
  },
  listItem: {
    marginTop: 10,
    paddingVertical: 20,
    paddingHorizontal: 20,
    backgroundColor: '#fff',
    flexDirection: 'row'
  },
  coverImage: {
    width: 100,
    height: 100,
    borderRadius: 8
  },
  metaInfo: {
    marginLeft: 10
  },
  title: {
    fontSize: 18,
    width: 200,
    padding: 10
  }
});
```

The following is the list of contacts displayed using a `FlatList` that we are going to get after this step.

<center><img src="https://blog.jscrambler.com/content/images/2020/08/jscrambler-blog-hooks-and-flatlist-react-native-3.png" alt="Hooks and Flatlist in React Native"></center>

Here is the loading indicator when the data is being fetched.

<center><img src="https://blog.jscrambler.com/content/images/2020/08/jscrambler-blog-hooks-and-flatlist-react-native-4.png" alt="Hooks and Flatlist in React Native"></center>

And below is the scenario when the app is unable to fetch the data.

<center><img src="https://blog.jscrambler.com/content/images/2020/08/jscrambler-blog-hooks-and-flatlist-react-native-5.png" alt="Hooks and Flatlist in React Native"></center>

## Add a search bar

In this section, let's create a search bar at the top of the current `FlatList`. It provides a prop called `ListHeaderComponent` to display a search bar.

Before we begin editing the `App` component, let us add the necessary import statements required in this step. From `react-native`, add the import for `TextInput`. Also, import `lodash.filter`.

```js
import {
  StyleSheet,
  Text,
  View,
  FlatList,
  ActivityIndicator,
  Image,
  TextInput
} from 'react-native';
import filter from 'lodash.filter';
```

Add the prop to the `FlatList` as shown below:

```js
<FlatList
  ListHeaderComponent={renderHeader}
  // ... rest of the props remain same
/>
```

Then define the `renderHeader` function that is going to return the following JSX:

```js
export default function App() {
  //...
  function renderHeader() {
    return (
      <View
        style={{
          backgroundColor: '#fff',
          padding: 10,
          marginVertical: 10,
          borderRadius: 20
        }}
      >
        <TextInput
          autoCapitalize="none"
          autoCorrect={false}
          clearButtonMode="always"
          value={query}
          onChangeText={queryText => handleSearch(queryText)}
          placeholder="Search"
          style={{ backgroundColor: '#fff', paddingHorizontal: 20 }}
        />
      </View>
    );
  }
// … render JSX below
}
```

Here is the output in the simulator after this step.

<center><img src="https://blog.jscrambler.com/content/images/2020/08/jscrambler-blog-hooks-and-flatlist-react-native-6.png" alt="Hooks and Flatlist in React Native"></center>

Next, add two more state variables. First, `query` is going to keep track of any input provided by the user to search through the list of data. It has a default value of empty string. Second, add another variable to hold the data from the API that is going to be used to filter the data.

```js
const [query, setQuery] = useState('');
const [fullData, setFullData] = useState([]);
```

Update the side-effect `useEffect` to populate the `fullData` array.

```js
useEffect(() => {
  setIsLoading(true);

  fetch(API_ENDPOINT)
    .then(response => response.json())
    .then(response => {
      setData(response.results);

      // ADD THIS
      setFullData(response.results);

      setIsLoading(false);
    })
    .catch(err => {
      setIsLoading(false);
      setError(err);
    });
}, []);
```

Then, add a handler method called `handleSearch` that is going to handle the search bar. By default, it is going to format the search term provided as a query to lowercase. The user's name is filtered from the state variable `fullData` while the state variable `data` stores the final results after the search to render the correct user.

The `contains` handler method is going to look for the query. It accepts two parameters, the first and last name of the user and the formatted query to lowercase from `handleSearch()`.

```js
const handleSearch = text => {
  const formattedQuery = text.toLowerCase();
  const filteredData = filter(fullData, user => {
    return contains(user, formattedQuery);
  });
  setData(filteredData);
  setQuery(text);
};

const contains = ({ name, email }, query) => {
  const { first, last } = name;

  if (first.includes(query) || last.includes(query) || email.includes(query)) {
    return true;
  }

  return false;
};
```

Now, in the simulator, a search query to get results based on that query.

<center><img src="https://blog.jscrambler.com/content/images/2020/08/jscrambler-blog-hooks-and-flatlist-react-native-7.gif" alt="Hooks and Flatlist in React Native"></center>

## Conclusion

The focus of this tutorial was to get you familiarized with different props that the `FlatList` component provides.

Do note that it is recommended to use a powerful search provider such as [Algolia](https://www.algolia.com/doc/guides/building-search-ui/going-further/native/react/#using-connectors-instead-of-widgets) when fetching data from an API endpoint for better results.

Finally, don't forget to pay special attention if you're developing commercial React Native apps that contain sensitive logic. You can protect them against code theft, tampering, and reverse engineering by following <a href="https://blog.jscrambler.com/how-to-protect-react-native-apps-with-jscrambler/?utm_source=dev.to&utm_medium=referral&utm_campaign=react-native-flatlist">this guide</a>.