Build a Dynamic Searchable Blog with React Native
Tags: reactnative, mobile, javascript, beginners
Published: 2024-07-07 21:56:26

In this article, we will explore how to build a React Native app that fetches data from an API, displays it, and includes a search functionality to filter through posts effortlessly.

Let’s dive into the code and uncover the magic behind creating a searchable blog using React Native.

-----------------------------------

## Setting Up the Project
Before we begin, make sure you have a React Native environment set up. You can follow the official [React Native documentation](https://reactnative.dev/docs/environment-setup) to get started.

```javascript
npx @react-native-community/cli@latest init MyBlogApp
cd MyBlogApp
```
Install necessary dependencies
```javascript
npm install
```

-----------------------------------

## Building the Posts Component
The core of our application is the `Posts` component. This component will handle fetching posts, displaying them, and providing a search functionality to filter through the posts.

First, I’ll create the UI components: a “Load Posts” button, a text input for filtering posts, and a FlatList for displaying the list of posts.

```javascript
import React, { useState } from "react";
import {
  Text,
  View,
  Pressable,
  FlatList,
  TextInput,
  StyleSheet,
} from "react-native";
import Post from "./Post";

const Posts = () => {
  const [posts, setPosts] = useState([]);
  const [filteredPosts, setFilteredPosts] = useState([]);
  const [loaded, setLoaded] = useState(false);
  const [searchText, setSearchText] = useState("");
  const url = "https://jsonplaceholder.typicode.com/posts";

  return (
    <View style={styles.mainContainer}>
      {!loaded ? (
        <Pressable
          style={styles.loadBtn}
          onPress={() => {}}
        >
          <Text>Load Posts</Text>
        </Pressable>
      ) : (
        <View>
          <View style={styles.searchContainer}>
            <Text>Search: </Text>
            <TextInput
              style={styles.searchField}
              value={searchText}
              onChangeText={() => {}}
            />
          </View>
          <FlatList
            data={filteredPosts}
            keyExtractor={(item) => item.id.toString()}
            renderItem={({ item }) => <Post post={item} />}
            ListEmptyComponent={<Text>No posts found.</Text>}
            contentContainerStyle={styles.postsContainer}
          />
        </View>
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  mainContainer: { flex: 1, padding: 30 },
  loadBtn: {
    borderWidth: 1,
    padding: 10,
    alignItems: "center",
    borderRadius: 5,
    backgroundColor: "#f0f0f0",
  },
  searchContainer: {
    flexDirection: "row",
    alignItems: "center",
    alignSelf: "center",
    marginBottom: 20,
  },
  searchField: {
    borderWidth: 1,
    height: 40,
    width: 200,
    borderRadius: 5,
    paddingHorizontal: 10,
  },
  postsContainer: { paddingHorizontal: 20, paddingVertical: 10 },
});

export default Posts;
```

-----------------------------------

## Get Posts from API
Now, let's write a function to get posts from the external API.
```javascript
  const getPosts = async () => {
    try {
      const response = await fetch(url);
      const result = await response.json();
      setPosts(result);
      setFilteredPosts(result);
      setLoaded(true);
    } catch (error) {
      console.error("Failed to fetch posts:", error);
    }
  };

...
  <Pressable
    style={styles.loadBtn}
    testID="load-posts"
    onPress={getPosts}
  >
    <Text>Load Posts</Text>
  </Pressable>
...
```

-----------------------------------

## Write Logic for Filtration
Here, I am checking the post title and body for the searched text and updating the displayed posts accordingly.
```javascript
  const filterPosts = (text) => {
    setSearchText(text);
    if (text) {
      const lowercasedText = text.toLowerCase();
      const updatedPosts = posts.filter(
        (post) =>
          post.title.toLowerCase().includes(lowercasedText) ||
          post.body.toLowerCase().includes(lowercasedText)
      );
      setFilteredPosts(updatedPosts);
    } else {
      setFilteredPosts(posts);
    }
  };

...
<TextInput 
  ...
  onChangeText={filterPosts}
/>
...
```

-----------------------------------

## Understanding the Code
- **State Management:** We use React’s `useState` to manage the state for posts, filtered posts, loading status, and search text.
- **Fetching Data:** The `getPosts` function fetches data from the JSONPlaceholder API and updates the posts state.
- **Filtering Posts:** The `filterPosts` function filters posts based on the search text. If the search text matches any part of the post title or body, it includes that post in the filtered list.
- **Rendering Posts:** Depending on whether there is search text, we display either the complete list of posts or the filtered list.

-----------------------------------

## Creating the Post Component
Create a `Post` component to display individual posts

```javascript
import React from "react";
import { View, Text, StyleSheet } from "react-native";

const Post = ({ post }) => {
  return (
    <View style={styles.postContainer}>
      <Text style={styles.postTitle}>{post.title}</Text>
      <Text>{post.body}</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  postContainer: { padding: 10, borderBottomWidth: 1, borderColor: "#ddd" },
  postTitle: { fontSize: 18, fontWeight: "bold" }
});

export default Post;
```

-----------------------------------

## Running the Application
To run your React Native app, use the following command
```javascript
npx react-native run-android
# or
npx react-native run-ios
```

-----------------------------------

## Demo

{% embed https://codesandbox.io/embed/d7spxt?view=editor+%2B+preview %}