Instagram Feed clone with React Native, UI Kitten and Firebase

2020-03-13

With React Native you can build cross-platform applications using JavaScript as the programming language. Each of your mobile apps may contain single or multiple user interfaces to serve a purpose.

Take, for example, Instagram. It is one of the most used mobile apps on both platforms that consists a different to serve main features such as sharing a photo and displaying it on the home screen, user's profile screen contains details about the user, activity screen contains a history all notifications that include likes or comments on each post.

What are we building

In this tutorial, we are going to build one of the user interfaces from the example of Instagram in React Native with Firebase backend service. The Firebase will allow us to upload and query a real time server to fetch images and display them in the app.

ss11

The complete source code for the demo app is available at this Github repo.

Stack/Requirements

I will not be covering how to install modules such as react-native-firebase or react-native-image-picker and connect its native bindings. Please refer to their official documentation for that.

Setting up Navigation and UI Kitten

Before you'd want to dwell on the rest of the tutorial, please make sure you have the following dependencies installed in your React Native project. Follow the commands in the sequence they are presented below.

1react-native init instacloneApp
2
3# after the project directory is created
4cd instacloneApp
5
6# install the following
7yarn add react-navigation react-native-svg react-native-screens@1.0.0-alpha.23 react-native-gesture-handler react-native-reanimated react-navigation-tabs react-navigation-stack react-native-ui-kitten @eva-design/eva @ui-kitten/eva-icons uuid react-native-image-picker react-native-firebase

We are using the latest version of react-native-cli at the time of writing this post with react-native version 0.61.2.

To integrate react-navigation library, please follow the appropriate set of instructions depending on your react-native version here.

react-native-ui-kitten does provide interactive documentation. Make sure to configure the application root from the docs here just to verify that its related dependencies have been installed correctly.

1import React from 'react'
2import { mapping, light as lightTheme } from '@eva-design/eva'
3import { ApplicationProvider, Layout, Text } from 'react-native-ui-kitten'
4
5const ApplicationContent = () => (
6 <Layout style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
7 <Text>Welcome to UI Kitten</Text>
8 </Layout>
9)
10
11const App = () => (
12 <ApplicationProvider mapping={mapping} theme={lightTheme}>
13 <ApplicationContent />
14 </ApplicationProvider>
15)
16
17export default App

You see, UI kitten library comes with a default light and dark theme that your app can switch between. Once you modify the App.js file to the following above code snippet, you will get the following result. You will have to open two tabs in your terminal window.

1# in the first window, run:
2yarn start
3
4# in the second window, depending on your development OS
5react-native run-ios
6
7# or
8
9react-native run-android

ss1

Creating a Tab Navigator

The Instagram app contains five different screens that are accessible from tab navigation. Let us try to implement that interface in the React Native app with five different screens that contain some dummy presentation to display.

Create the src/ directory and inside it create a new folder called screens/. This folder will contain the following five screens.

  • Feed.js
  • Search.js
  • AddPost.js
  • Activity.js
  • Profile.js

For now, you can add a dummy presentation component that just lists the screen name at the center when it is being currently viewed in the app. For example, the file Feed.js will look like below:

1import React from 'react'
2import { Text, Layout } from 'react-native-ui-kitten'
3
4const Feed = () => (
5 <Layout style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
6 <Text>Feed Screen</Text>
7 </Layout>
8)
9
10export default Feed

The screens/ directory will look like as below with five different files.

ss2

Next, create a new file TabNavigator.js inside src/navigation directory. Import the required libraries and all the five screens.

1import React from 'react'
2import { createAppContainer } from 'react-navigation'
3import { createBottomTabNavigator } from 'react-navigation-tabs'
4
5import Feed from '../screens/Feed'
6import Search from '../screens/Search'
7import AddPost from '../screens/AddPost'
8import Activity from '../screens/Activity'
9import Profile from '../screens/Profile'

Eva design system comes with open source icon library that we are going to use in this tutorial. You are free to use any other icon library as well.

Since the 4.x version of react-navigation library, all navigation patterns are separated in their npm packages.

Let us create a simple tab bar on the bottom of the screen with the following route configs.

1const TabNavigator = createBottomTabNavigator({
2 Feed: {
3 screen: Feed
4 },
5 Search: {
6 screen: Search
7 },
8 AddPost: {
9 screen: AddPost
10 },
11 Activity: {
12 screen: Activity
13 },
14 Profile: {
15 screen: Profile
16 }
17})
18
19export default createAppContainer(TabNavigator)

Using react-navigation, routes are lazily initialized by default. This means any screen component is not mounted until it becomes active first.

To integrate this tab navigator, open App.js file and modify it:

1import React from 'react'
2import { mapping, light as lightTheme } from '@eva-design/eva'
3import { ApplicationProvider } from 'react-native-ui-kitten'
4
5import TabNavigator from './src/navigation/TabNavigator'
6
7const App = () => (
8 <ApplicationProvider mapping={mapping} theme={lightTheme}>
9 <TabNavigator />
10 </ApplicationProvider>
11)
12
13export default App

Here is the output:

ss3

The tab bar displays the name of the screen component.

Adding icons to tab bar

Instead of displaying names for each screen, let us display the appropriate icons. We have already installed the icon library. Modify App.js file to integrate icons from @ui-kitten/eva-icons which can be configured using IconRegistery.

1import React, { Fragment } from 'react'
2import { mapping, light as lightTheme } from '@eva-design/eva'
3import { ApplicationProvider, IconRegistry } from 'react-native-ui-kitten'
4import { EvaIconsPack } from '@ui-kitten/eva-icons'
5
6import TabNavigator from './src/navigation/TabNavigator'
7
8const App = () => (
9 <Fragment>
10 <IconRegistry icons={EvaIconsPack} />
11 <ApplicationProvider mapping={mapping} theme={lightTheme}>
12 <TabNavigator />
13 </ApplicationProvider>
14 </Fragment>
15)
16
17export default App

Note that, if you are planning to use third party icons library such as react-native-vector-icons you can learn more here on how to integrate that. Next, go to TabNavigator.js file. First, import the Icon component from react-native-ui-kitten.

1import { Icon } from 'react-native-ui-kitten'

Each route in the BottomTabNavigator has access to different properties via navigationOptions object. To hide the label or the name of each screen and display an icon in place of it is achieved by returning an Icon component on tabBarIcon property inside navigationOptions.

Also, when a specific route or the screen is focused, its icon colour should appear darker than the other icons in the tab bar just to indicate that it is the active tab. This can be achieved using the prop focused on tabBarIcon.

Modify the tab navigator as the following:

1const TabNavigator = createBottomTabNavigator(
2 {
3 Feed: {
4 screen: Feed,
5 navigationOptions: {
6 tabBarIcon: ({ focused }) => (
7 <Icon
8 name="home-outline"
9 width={32}
10 height={32}
11 fill={focused ? '#111' : '#939393'}
12 />
13 )
14 }
15 },
16 Search: {
17 screen: Search,
18 navigationOptions: {
19 tabBarIcon: ({ focused }) => (
20 <Icon
21 name="search-outline"
22 width={32}
23 height={32}
24 fill={focused ? '#111' : '#939393'}
25 />
26 )
27 }
28 },
29 AddPost: {
30 screen: AddPost,
31 navigationOptions: {
32 tabBarIcon: ({ focused }) => (
33 <Icon
34 name="plus-square-outline"
35 width={32}
36 height={32}
37 fill={focused ? '#111' : '#939393'}
38 />
39 )
40 }
41 },
42 Activity: {
43 screen: Activity,
44 navigationOptions: {
45 tabBarIcon: ({ focused }) => (
46 <Icon
47 name="heart-outline"
48 width={32}
49 height={32}
50 fill={focused ? '#111' : '#939393'}
51 />
52 )
53 }
54 },
55 Profile: {
56 screen: Profile,
57 navigationOptions: {
58 tabBarIcon: ({ focused }) => (
59 <Icon
60 name="person-outline"
61 width={32}
62 height={32}
63 fill={focused ? '#111' : '#939393'}
64 />
65 )
66 }
67 }
68 },
69 {
70 tabBarOptions: {
71 showLabel: false
72 }
73 }
74)

To display an Icon from the UI Kitten, it is required to provide attributes such as width and height.

The createBottomTabNavigator accepts the second parameter as config object to modify the whole tab bar rather than each route. tabBarOptions is an object with different properties such hiding the label of each route by setting the boolean value of showLabel to false.

Adding a Header to Feed screen

Since the Feed route is going to be the first screen that a user will see when they open the app, let us display the name of the application in a header at the top. Also, this header will serve the purpose of navigating to a different route later (such as Camera). This route that we are going to create later is only going to be accessible from the Feed screen and has nothing to do with the Tab bar. Thus, let us create a new stack navigator for the Feed screen separate and then add that in the TabNavigator.

Create a new file StackNavigator inside navigation/ directory.

1import React from 'react'
2import { createAppContainer } from 'react-navigation'
3import { createStackNavigator } from 'react-navigation-stack'
4import Feed from '../screens/Feed'
5
6export const FeedNavigator = createAppContainer(
7 createStackNavigator({
8 Feed: {
9 screen: Feed,
10 navigationOptions: {
11 headerTitle: 'Social App'
12 }
13 }
14 })
15)

Modify TabNavigator.js file and replace the Feed screen with FeedNavigator. Import it first.

1// after other import statements
2import { FeedNavigator } from './StackNavigator'

Then, replace the value of screen with FeedNavigator.

1Feed: {
2 screen: FeedNavigator,
3 //... rest remains same
4}

ss5

Create Feed UI

Let us begin by creating a simple UI for the Feed screen that will contain the image, title of the image, user avatar, and description of the image post. To begin, open Feed.js file and import the following elements from react-native and react-native-ui-kitten.

1import React, { Component } from 'react'
2import { Image, View, TouchableOpacity } from 'react-native'
3import { Text, Avatar, withStyles, List } from 'react-native-ui-kitten'

Right, we are going to fetch some posts by mocking a DATA array. Add this before the Feed component.

1const DATA = [
2 {
3 id: 1,
4 postTitle: 'Planet of Nature',
5 avatarURI:
6 'https://images.unsplash.com/photo-1559526323-cb2f2fe2591b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80',
7 imageURI:
8 'https://images.unsplash.com/photo-1482822683622-00effad5052e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80',
9 randomText:
10 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. '
11 },
12 {
13 id: 2,
14 postTitle: 'Lampost',
15 avatarURI:
16 'https://images.unsplash.com/photo-1559526323-cb2f2fe2591b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80',
17 imageURI:
18 'https://images.unsplash.com/photo-1482822683622-00effad5052e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80',
19 randomText:
20 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. '
21 }
22]

The List from React Native UI Kitten extends the basic FlatList from react-native to render a list of items. In a real application having a Flat list is useful instead of ScrollView when there is a large number of data items in the list to render to the user.

It accepts the same amount of props as a normal flat list component. Return the following:

1return (
2 <List
3 style={this.props.themedStyle.container}
4 data={DATA}
5 renderItem={renderItem}
6 keyExtractor={DATA.id}
7 />
8)

We will come back to the style attribute in the next section. The data attribute accepts the value of a plain array, hence the mock DATA. Using keyExtractor gives the List to extract a unique key for each item in the list that is rendered. The renderItem attribute accepts what to display in the list, or how to render the data.

React Native UI kitten has a default ListItem component that you can use to display items but since we need customization, let us create our own. Add the following inside the render method of the component but before the return statement.

1const renderItem = ({ item }) => (
2 <View style={this.props.themedStyle.card}>
3 <Image
4 source={{ uri: item.imageURI }}
5 style={this.props.themedStyle.cardImage}
6 />
7 <View style={this.props.themedStyle.cardHeader}>
8 <Text category="s1" style={this.props.themedStyle.cardTitle}>
9 {item.postTitle}
10 </Text>
11 <TouchableOpacity
12 onPress={() => this.props.navigation.navigate('Profile')}
13 >
14 <Avatar
15 source={{ uri: item.avatarURI }}
16 size="small"
17 style={this.props.themedStyle.cardAvatar}
18 />
19 </TouchableOpacity>
20 </View>
21 <View style={this.props.themedStyle.cardContent}>
22 <Text category="p2">{item.randomText}</Text>
23 </View>
24 </View>
25)

The Avatar and Text are both Ui components provided by the UI Kitten library. Avatar is styled Image component as well as is Text. In the above snippet, notice how the category='p2' attribute is being used on the Text. UI Kitten provides these specific styles. You can explore more about it here.

Adding styles with High Order Function

UI Kitten library provides a themed base design system that you can customize to your needs in form of a JSON object. It provides theme variables that can help you create custom themes based on some initial values and support React Native style properties at the same time.

This section will showcase, how you can integrate its theme using a High Order Function in a React Native screen and with dwelling much into customization. You can read more it here.

We have already imported withStyles HOC from UI Kitten. It accepts a component that can use the theme variables. In our case the Feed component.

First, just to identify the class component it accepts and the one it returns, edit the following line.

1class _Feed extends Component {
2 // ...
3}

Add the following style while exporting the Feed component. These styles can be used in the style as props (which you have seen in the previous section).

1export default Feed = withStyles(_Feed, theme => ({
2 container: {
3 flex: 1
4 },
5 card: {
6 backgroundColor: theme['color-basic-100'],
7 marginBottom: 25
8 },
9 cardImage: {
10 width: '100%',
11 height: 300
12 },
13 cardHeader: {
14 padding: 10,
15 flexDirection: 'row',
16 alignItems: 'center',
17 justifyContent: 'space-between'
18 },
19 cardTitle: {
20 color: theme['color-basic-1000']
21 },
22 cardAvatar: {
23 marginRight: 16
24 },
25 cardContent: {
26 padding: 10,
27 borderWidth: 0.25,
28 borderColor: theme['color-basic-600']
29 }
30}))

Here is the output you get.

ss6

Create a Firebase Context

Before proceeding with this section, please make sure you have successfully followed instructions to install and integrate react-native-firebase library in your React Native app. Also, you have set up a Firebase app and have the right to access Firestore.

Using Context API you can easily manage to consume Firebase methods in the app without adding a state management library like Redux.

The common reason to use Context API in a React Native app is that you need to share some data in different places or components in the component tree. Manually passing props can be tedious as well as hard to keep track of.

The Context API consists of three building blocks:

  • creating a context object
  • declaring a provider that gives the value
  • declaring a consumer that allows a value to be consumed (provided by the provider)

Create utils directory in src and add a new file Firebase.js. This file will contain two methods that will handle to upload an image with relevant post data to the Firestore in a collection called post. The second method is used to fetch all the posts from the collection.

Using uuid package you can create a unique identifier for each post uploaded.

1import firebase from 'react-native-firebase'
2import uuid from 'uuid'
3
4const Firebase = {
5 uploadPost: post => {
6 const id = uuid.v4()
7 const uploadData = {
8 id: id,
9 postPhoto: post.photo,
10 postTitle: post.title,
11 postDescription: post.description,
12 likes: []
13 }
14 return firebase
15 .firestore()
16 .collection('posts')
17 .doc(id)
18 .set(uploadData)
19 },
20 getPosts: () => {
21 return firebase
22 .firestore()
23 .collection('posts')
24 .get()
25 .then(function(querySnapshot) {
26 let posts = querySnapshot.docs.map(doc => doc.data())
27 // console.log(posts)
28 return posts
29 })
30 .catch(function(error) {
31 console.log('Error getting documents: ', error)
32 })
33 }
34}
35
36export default Firebase

Next, create a new file called FirebaseContext.js. It will hold the snippet for creating the context and a High Order Function. The HoC will eliminate the need for importing and using Firebase.The consumer in every component necessary. By wrapping each component as a parameter to the HoC will provide access to Firebase queries (or the custom methods created in file Firebase.js) as props.

1import React, { createContext } from 'react'
2
3const FirebaseContext = createContext({})
4
5export const FirebaseProvider = FirebaseContext.Provider
6
7export const FirebaseConsumer = FirebaseContext.Consumer
8
9export const withFirebaseHOC = Component => props => (
10 <FirebaseConsumer>
11 {state => <Component {...props} firebase={state} />}
12 </FirebaseConsumer>
13)

Create a new file index.js to export both the Firebase object from the Firebase.js file, the provider and the HoC.

1import Firebase from './Firebase'
2import { FirebaseProvider, withFirebaseHOC } from './FirebaseContext'
3
4export default Firebase
5
6export { FirebaseProvider, withFirebaseHOC }

The provider has to grab the value from the context object for the consumer to use that value. This is going to be done in the App.js file. The value for the FirebaseProvider is going to be the Firebase object.

1import React, { Fragment } from 'react'
2import { mapping, light as lightTheme } from '@eva-design/eva'
3import { ApplicationProvider, IconRegistry } from 'react-native-ui-kitten'
4import { EvaIconsPack } from '@ui-kitten/eva-icons'
5
6import Firebase, { FirebaseProvider } from './src/utils'
7import TabNavigator from './src/navigation/TabNavigator'
8
9const App = () => (
10 <Fragment>
11 <IconRegistry icons={EvaIconsPack} />
12 <ApplicationProvider mapping={mapping} theme={lightTheme}>
13 <FirebaseProvider value={Firebase}>
14 <TabNavigator />
15 </FirebaseProvider>
16 </ApplicationProvider>
17 </Fragment>
18)
19
20export default App

Uploading images to Firestore

Let us add modify the AddPost component to let the user choose an image from the phone's gallery and store it on the Firestore database. Open the AddPost.js file and add the following import statements.

1import React, { Component } from 'react'
2import { Image, View } from 'react-native'
3import { Text, Button, Input } from 'react-native-ui-kitten'
4import ImagePicker from 'react-native-image-picker'
5import { withFirebaseHOC } from '../utils'

Next, in the class component, add a state object that will track when the image file is picked from the gallery as well as when there are a title and a description provided for the image file. All of these three combined will create one post. You have seen the same in mock DATA array in Feed.js previously.

Using ImagePicker.launchImageLibrary() from react-native-image-picker an image is picked. Do note that, this method expects an options object as the parameter. If an image is picked successfully, it will provide the URI of the image.

The onSubmit asynchronous method is responsible to upload the post to the Firestore and clear the state object when the post is successfully uploaded.

1class AddPost extends Component {
2 state = { image: null, title: '', description: '' }
3
4 onChangeTitle = title => {
5 this.setState({ title })
6 }
7 onChangeDescription = description => {
8 this.setState({ description })
9 }
10
11 onSubmit = async () => {
12 try {
13 const post = {
14 photo: this.state.image,
15 title: this.state.title,
16 description: this.state.description
17 }
18 this.props.firebase.uploadPost(post)
19
20 this.setState({
21 image: null,
22 title: '',
23 description: ''
24 })
25 } catch (e) {
26 console.error(e)
27 }
28 }
29
30 selectImage = () => {
31 const options = {
32 noData: true
33 }
34 ImagePicker.launchImageLibrary(options, response => {
35 if (response.didCancel) {
36 console.log('User cancelled image picker')
37 } else if (response.error) {
38 console.log('ImagePicker Error: ', response.error)
39 } else if (response.customButton) {
40 console.log('User tapped custom button: ', response.customButton)
41 } else {
42 const source = { uri: response.uri }
43 console.log(source)
44 this.setState({
45 image: source
46 })
47 }
48 })
49 }
50
51 render() {
52 return (
53 <View style={{ flex: 1, marginTop: 60 }}>
54 <View>
55 {this.state.image ? (
56 <Image
57 source={this.state.image}
58 style={{ width: '100%', height: 300 }}
59 />
60 ) : (
61 <Button
62 onPress={this.selectImage}
63 style={{
64 alignItems: 'center',
65 padding: 10,
66 margin: 30
67 }}
68 >
69 Add an image
70 </Button>
71 )}
72 </View>
73 <View style={{ marginTop: 80, alignItems: 'center' }}>
74 <Text category="h4">Post Details</Text>
75 <Input
76 placeholder="Enter title of the post"
77 style={{ margin: 20 }}
78 value={this.state.title}
79 onChangeText={title => this.onChangeTitle(title)}
80 />
81 <Input
82 placeholder="Enter description"
83 style={{ margin: 20 }}
84 value={this.state.description}
85 onChangeText={description => this.onChangeDescription(description)}
86 />
87 <Button status="success" onPress={this.onSubmit}>
88 Add post
89 </Button>
90 </View>
91 </View>
92 )
93 }
94}
95
96export default withFirebaseHOC(AddPost)

Do not forget to wrap the component inside withFirebaseHOC. You will get the following screen.

ss7

Click on the button Add an image and choose the image from the device's gallery or stored images.

ss8

By clicking the button Add post the post will be submitted to Firestore which you can verify by opening the Firebase console. You will find a posts collection. As an example is shown below:

ss9

Fetching posts from Firestore

From second to the previous section, you have observed that we are saving each post under a unique id as the name of the document under a collection called posts. To fetch all these documents, you will have to query the Firestore.

In the file utils/Firebase.js the function getPosts() does that for you. Using querySnapshot.docs.map you can fetch multiple documents at once from the Firestore database. All of these posts are going to be shown at the Feed screen which is the entry point of the application. Right now, it only shows some mock data.

Open Feed.js and import the following statements.

1import React, { Component } from 'react'
2import { Image, View, TouchableOpacity, ActivityIndicator } from 'react-native'
3import { Text, Avatar, withStyles, List } from 'react-native-ui-kitten'
4import { withFirebaseHOC } from '../utils'

Next, in the class component, create a state object with two properties. The first property DATA is going to hold the array of all documents. The second property isRefreshing is going to be used in List to implement the functionality of fetching new results at pull to refresh.

1class _Feed extends Component {
2 state = { DATA: null, isRefreshing: false }
3 // ...
4}

Next, create a handler method called fetchPosts to fetch the data. Also, you have to explicitly call this method in the lifecycle method componentDidMount to load all posts available since Feed is the entry screen.

1componentDidMount() {
2 this.fetchPosts()
3 }
4
5 fetchPosts = async () => {
6 try {
7 const posts = await this.props.firebase.getPosts()
8 console.log(posts)
9 this.setState({ DATA: posts, isRefreshing: false })
10 } catch (e) {
11 console.error(e)
12 }
13 }

Next, add another method called onRefresh that is responsible to fetch posts when the screen is pulled downwards.

1onRefresh = () => {
2 this.setState({ isRefreshing: true })
3 this.fetchPosts()
4}

Here is how the rest of the component will look like. While the data is being currently fetched, it will show a loading indicator on the screen.

1render() {
2 const renderItem = ({ item }) => (
3 <View style={this.props.themedStyle.card}>
4 <Image
5 source={{ uri: item.postPhoto.uri }}
6 style={this.props.themedStyle.cardImage}
7 />
8 <View style={this.props.themedStyle.cardHeader}>
9 <Text category='s1' style={this.props.themedStyle.cardTitle}>
10 {item.postTitle}
11 </Text>
12 <TouchableOpacity
13 onPress={() => this.props.navigation.navigate('Profile')}>
14 <Avatar
15 source={{
16 uri:
17 'https://images.unsplash.com/photo-1559526323-cb2f2fe2591b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80'
18 }}
19 size='small'
20 style={this.props.themedStyle.cardAvatar}
21 />
22 </TouchableOpacity>
23 </View>
24 <View style={this.props.themedStyle.cardContent}>
25 <Text category='p2'>{item.postDescription}</Text>
26 </View>
27 </View>
28 )
29
30 if (this.state.DATA != null) {
31 return (
32 <List
33 style={this.props.themedStyle.container}
34 data={this.state.DATA}
35 renderItem={renderItem}
36 keyExtractor={this.state.DATA.id}
37 refreshing={this.state.isRefreshing}
38 onRefresh={() => this.onRefresh()}
39 />
40 )
41 } else
42 return (
43 <View
44 style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
45 <ActivityIndicator size='large' />
46 </View>
47 )
48 }

Lastly, wrap it up with the Firebase HOC.

1export default Feed = withFirebaseHOC(
2 withStyles(_Feed, theme => ({
3 container: {
4 flex: 1
5 },
6 card: {
7 backgroundColor: theme['color-basic-100'],
8 marginBottom: 25
9 },
10 cardImage: {
11 width: '100%',
12 height: 300
13 },
14 cardHeader: {
15 padding: 10,
16 flexDirection: 'row',
17 alignItems: 'center',
18 justifyContent: 'space-between'
19 },
20 cardTitle: {
21 color: theme['color-basic-1000']
22 },
23 cardAvatar: {
24 marginRight: 16
25 },
26 cardContent: {
27 padding: 10,
28 borderWidth: 0.25,
29 borderColor: theme['color-basic-600']
30 }
31 }))
32)

On the initial load, since there is only one post in the posts collection, the output will be the following:

ss10

Try adding one more post now and use pull to refresh to fetch the latest document from the posts collection.

ss11

Conclusion

This brings an end to this current tutorial. There are many useful strategies for using Firebase and React Native together that you can take from this post. Also, using a UI library like react-native-ui-kitten saves a lot of time to figure out how to style each component.

The Feed screen was implemented is from one of the templates from Crowdbotics' react-native collection. We use UI Kitten for our latest template libraries. You can modify the screen further, add another component that takes care of counting likes or comments. Find more about how to create custom screens like this from our open source project here.


Originally published at Crowdbotics' Blog.

I'm Aman working as an independent fullstack developer with technologies such as Node.js, ReactJS, and React Native. I try to document and write tutorials to help JavaScript, Web and Mobile developers.