React Native Async Image Loading with Firebase Storage

Updated: Mar 25



In one of the recent apps, I had to figure out a way to load images asynchronously from Firebase Storage. While there are a few components out there most of them had a quirk or two. After trying them, I decided to build what worked for me. While it may not be perfect, it provides a base for others to build upon. So sharing it here.


Before Getting Started

I use the React-Native-Firebase from Invertase. It is one of the best libraries out there to simplify firebase integration. Make sure you have that installed. The steps to get that setup are all at this site. So we won’t duplicate them. You can install it from this link.


Create a Component called AsyncImage

In your project, create a new component called AsyncImage. You can do this by simply creating a JS file called AsyncImage.js and copying and pasting the following code.


In the snippet below, we import react, react-native-firebase, and other components such as View, ActivityIndicator, and Image from the react-native library. Then create a custom component class called “AsyncImage” and create the “shell” that you can use to build your component. Before you proceed, review the code comments inline to understand how each of the function was used.

import React from 'react';
import * as fabfirebaseapp from 'react-native-firebase';
import { View, ActivityIndicator, Image} from 'react-native';
export default class AsyncImage extends React.Component {
//The constructor for your component
constructor(props){
     super(props)
}
//The code that is called when the component is first mounted. Use it to setup the component and load the image files
componentDidMount() {
}
//The code that is called when the component is about to unmount. Use it to cancel any http calls otherwise you will get a memory warning from React
componentWillUnmount() {
}
//The code that is called when the props passed to the component change. It is typically useful when say you are implementing a search functionality and you need to refresh the image after the component is refreshed on the component.
componentWillReceiveProps(props) {
}
//The render function to display the image
render() {
}
}
 

Populate your component (The real code)

First, in the constructor let us set the state variables.

constructor(props) {
   super(props);
   this.state =
   {
       loading: true,
       mounted: true,
       image: "/images/logoblue.jpg",
       url: "",
   }
}

Then in the componentDidMount let’s load the image. We will write a custom function called getAndLoadHttpUrl(). This function is the heart of this entire component. We first check if the component is mounted, look to access a prop called “image”. This prop can be anything you want it to be and it should be used by the component to provide a reference to the item in firebase storage. We then call the getDownloadURL() function from the react-native-firebase, set the URL prop, complete the loading. If the loading results in an error, show a default image.


componentDidMount() {
   this.setState({ isMounted: true })
   this.getAndLoadHttpUrl()
}
async getAndLoadHttpUrl() {

   if (this.state.mounted == true) {
     const ref = fabfirebaseapp.storage().ref(this.props.image);
     ref.getDownloadURL().then(data => {
        this.setState({ url: data })
        this.setState({ loading: false })
     }).catch(error => {
        this.setState({ url: "/images/logoblue.jpg" })
        this.setState({ loading: false })
    })
  }
} 

Next, let’s look at the componentWillUnmount(). We basically tell the component code that it is unmounted. This will ensure that there is no more new download functions are called.

componentWillUnmount() {
   this.setState({ isMounted: false })
} 

You can then look at componentWillReceiveProps(props). This is a very useful function especially if someone uses your component to conduct a search or would like to do something when a prop changes. In this case, we actually expect a custom prop called refresh and if the component using this component wants to refresh, we can execute whatever we want. In this case, I just don’t do anything.


componentWillReceiveProps(props) {
this.props = props
if (this.props.refresh == true) {
//Write whatever code you want here.
}
}

Let’s render the image now. We check if the image is mounted and loading then we return an Activity Indicator. If the image loads, then we render the image. If the component is not mounted we don’t render anything.

render() {
    if (this.state.mounted == true) {
       if (this.state.loading == true) {
return (
       <View key={this.props.image} style={{ width: '100%', alignItems: 'center', justifyContent: 'center' }} >
      <ActivityIndicator />
   </View>
)
}
else {
return (
    <Image style={this.props.style} source={{uri: this.state.url}}/>
)
}
}
else {
    return null
}
} 


Improving upon this code

The code here renders the image as soon as the URL from firebase is available. It doesn’t check if the actual image loads. It works for me because the images I am loading are extremely small in size and it is actually fast. You can continue to better it by reworking the getAndLoadHttpUrl to check if all the bytes of the image successfully loaded.


That’s it

You can then use this component anywhere you want in your app using the following snippet.


    
import AsyncImage from '../components/AsyncImage';
<AsyncImage image={this.state.logo} style={{width: '100%', height: 50, resizeMode: 'contain'}} refresh={this.state.refresh}></AsyncImage> 

I hope you find it helpful. The github gist is available here.

#Firebase #Coding #ReactNative

73 views
  • White Facebook Icon
  • Twitter
  • LinkedIn

© 2020 by ForNextCo.

Contact: Mani@ForNextCo.com