In this article, I want to share my experience of building custom React Hooks to fetch data from Firebase Firestore. I will assume that you already have a basic understanding of React Hooks. If you are entirely new to React Hooks, you can learn the basic of React Hooks in my previous
article.
Why React Hooks?
A quick refresher on React Hooks, Hooks enable you to define stateful logic as reusable functions that can be used throughout your React application. Hooks also allow function components to bind into the component lifecycle, earlier only achievable with class components.
When it comes to building components that need to manage lifecycle events, React does not suggest whether you should use function components and Hooks or old-fashioned class components.
That being said, function components and Hooks have suddenly become a trending topic in the React developer community. Function components and Hooks significantly diminish the amount code and verbosity of a React app compared to class components.
Get started
To understand how to use React Hooks to fetch data from Firebase Firestore, we will build a simple function which will fetch a list of Instagram posts and listen to changes. You can bootstrap your project using
create-react-app or
create-next-app. I will also assume that you already understand how to add Firebase into your project dependencies and initialise it as this article will mainly focus on building the custom React Hooks.
First, open your project and create a new file called usePosts.ts
.
function usePosts() {}
export default usePosts;
Custom Hooks are more of a convention than a feature. If a function's name starts with "use
" and it calls other Hooks, we say it is a custom Hook. The useSomething
naming convention is how our linter plugin can find bugs in the code using Hooks.
Open your newly created custom Hook and define the state to manage the data coming from Firestore. Update your custom Hook to look like this:
import React from 'react';
interface Data {
error: Error | null;
loading: boolean;
posts: Post[];
}
interface Post {
id: string;
caption: string;
imageURL: string;
totalComments: number;
totalLikes: number;
}
function usePosts() {
const [data, setData] = React.useState<Data>({
error: null,
loading: true,
posts: [],
});
return data;
}
export default usePosts;
At this stage, when we use our custom Hook, it will always return the default data as we haven't attached any function that will fetch data from Firestore. Without wasting more time, let's start writing our function to fetch data from Firestore. Update your custom Hook to look like this:
import React from 'react';
// Firebase
import firebase from '../firebase/client';
interface Data {
error: Error | null;
loading: boolean;
posts: Post[];
}
interface Post {
id: string;
caption: string;
imageURL: string;
totalComments: number;
totalLikes: number;
}
function usePosts() {
const [data, setData] = React.useState<Data>({
error: null,
loading: true,
posts: [],
});
React.useEffect(() => { // <--- 1
const unsubscribe = firebase // <--- 2
.firestore()
.collection('posts')
.onSnapshot(
(snapshot) => {
setData({
error: null,
loading: false,
posts: snapshot.docs.map((doc) => ({
id: doc.id,
caption: doc.data().caption,
imageURL: doc.data().imageURL,
totalComments: doc.data().totalComments,
totalLikes: doc.data().totalLikes,
})),
}); // <--- 3
},
(error) => {
setData({
error,
loading: false,
posts: [],
}); // <---4
},
);
return unsubscribe; // <--- 5
}, []);
return data;
}
export default usePosts;
Code explanations:
- We will use
useEffect
Hook to handle data fetching from Firestore. This useEffect
Hook will be executed when our custom Hook is mounted. - We create a Firestore listener (snapshot) to fetch and listen to data changes in
posts
collection. This onSnapshot
function will return a function that can be used to unsubscribe from the data changes subscription. - When we get the data returned from Firestore, we map the data and update our Hook state accordingly. We will set the
loading
state to false
and posts
to list of posts coming from Firestore. Any components that implement this usePosts
Hook will get the state updates. - When there is any issue (e.g. insufficient permission or network issue), Firestore will return an error object. Our custom Hook is responsible to update its state to reflect this. We will set the
error
state to the error object coming from Firestore and loading
state to false
. Any components that implement this usePosts
Hook will get the state updates. - When this
usePosts
Hook is unmounted, it will unsubscribe from future incoming data changes.
To use this usePosts
Hook, you can simply call usePosts
Hook in any function components.
import React from 'react';
// Hooks
import usePosts from '../hooks/usePosts';
const Component: React.FC = () => {
const { error, loading, posts } = usePosts();
console.log('error:', error);
console.log('loading:', loading);
console.log('posts:', posts);
return <div>Hello, Custom Hook!</div>;
};
export default Component;
What's next?
We have explored a few of the built-in React Hooks, such as
useState
and
useEffect
, but there are still others that could help you as you develop your application. The
React documentation explains all the built-in Hooks very clearly.
We have explored one of the Firebase web APIs that let you fetch and listen to data changes from Firestore, but there are many other things you can do with the API. Try exploring the
Firebase documentation for a deeper understanding of Firebase APIs.
- How to make strongly-typed React components
TypeScript helps developers to improve their productivity and prevent them from producing bugs.
Published on Mar 10, 2021 · 2 min read
- Basic overview on JavaScript Promise
A promise represents a value that is unknown now that may become known in the future, in other words an asynchronous value.
Published on Mar 10, 2021 · 2 min read
- React Native over-the-air app update with CodePush
CodePush is a tool developed by Microsoft which allows developers to quickly and easily manage and update React Native JavaScript bundles over the air.
Published on Nov 1, 2020 · 7 min read
- Building a simple login form in React Native using React Hook Form
A short step-by-step tutorial about how to build a simple login form in React Native using React Hook Form.
Published on Oct 6, 2020 · 9 min read
- Building floating logo bubbles on Stripe.com using React Hooks
In this tutorial, we are going to clone animating logo bubbles from Stripe.com using React Hooks.
Published on Aug 11, 2020 · 9 min read
- Building a simple login form with Material UI and React Hook Form
In this article, I will give a short step-by-step tutorial about how to build a simple login form with Material UI and React Hook Form.
Published on Jul 23, 2020 · 7 min read
- Introduction to React Hooks
Hooks solve a wide variety of seemingly unconnected problems in React. Whether you're new to React or use it daily, you might recognise some of these problems.
Published on Jul 20, 2020 · 3 min read
- N+1 problem in GraphQL
The n+1 problem occurs because GraphQL executes a separate function – called resolver – for each field.
Published on Jul 17, 2020 · 2 min read
- GraphQL vs REST
GraphQL has been introduced as a revolutionary alternative to REST APIs. However, it has its pros and cons.
Published on Jul 16, 2020 · 3 min read
- Data fetching in Next.js 9.3+
There are two forms of pre-rendering: Static Generation and Server-side Rendering.
Published on Jul 15, 2020 · 6 min read
- When should we consider using third-party libraries?
The most crucial advantage of using third-party libraries is that it helps you to save time because you do not need to develop the functionality that the library gives.
Published on Jul 13, 2020 · 4 min read
- Step-by-step guidelines to implement Material UI in Next.js 2020
In this article, I want to share how to integrate Material UI in Next.js applications. We will build a very basic website using Material UI and Next.js.
Published on Jul 11, 2020 · 4 min read