Using Components to Bootstrap Data for your App
Tags: javascript, webdev, react, firebase
Published: 2021-04-30 21:09:35

Have you ever discovered something that just made stuff click? Something so game-changing that it brightened the colors in your world almost instantly? I found one of those recently, and I wanted to share it!

What I'm referring to here is a little "hack" you can use to bootstrap data for your application to help ensure the data is where it needs to be, when it needs to be there. I believe this is a common pattern, but I'm unsure of what that specific component is referred to as- so we'll call it the bootstrapping component.

On a zoomed-out level, this component sits in your component tree and quietly fetches data anytime the app hits a refresh, page re-direct-- all of those situations where you'd expect some data from your state to disappear.

## How it works
Here's how the bootstrapping component works on a very basic level.
1. It sits in the component tree
2. Data is fetched/mutated/merged (whatever you need it to do)
3. Data is placed into whatever state management tool you're using, making it available where needed.

This is obviously a very simplistic view of how it works, but if you had to elevator pitch this to someone, then this may be a good way to frame it.

### Into the weeds
Now let's get into how you would build one of these. In this example, we're going to be looking at building it in a [NextJS app](https://nextjs.org/) that's set up with Firebase. *This pattern is tool-agnostic and will work with a wide array of setups*

We're jumping in at the point after Firebase is already set up in this project.

#### Creating the Component
We'll start by creating a component in the /store directory. Components are usually placed in a `components/` folder, but this specific type of component doesn't care about what's being rendered, and in fact, doesn't render anything other than children passed to it- so we'll keep it in a different directory for clarity.

It's good practice to name these components similar to the data they'll be responsible for. We'll be using it to fetch user profile info in this case, so we'll call ours `UserProfileProvider`.

```javascript
'store/UseProfileProvider.js'

import { useEffect } from 'react'

const UserProfileProvider = ({ children }) => {
  useEffect(() => {
    
  }, [])

  return <>{children}</>
}

export default UserProfileProvider
```
*We'll need a useEffect later, so we'll go ahead and place an empty one for now*

#### Placing the Component
Now that we have our component setup, let's place it in the component tree. We can focus on our bootstrapping component and the data it needs to handle once that is done.

Let's jump over to our `_app.js` in the `/pages` directory. You'll see we already have an `AuthProvider` component. This component is doing something similar to what we're building here, but it's specifically handling user auth- which can get complex quickly. That's an article for another day.

So, in our `_app.js` You'll see there's already a component tree forming from different pieces needed for the app. We're going to place our `UseProfileProvider` component as high up as we can, but _within_ the `AuthProvider` component.

``` javascript
'pages/_app.js'

return (
    <AuthProvider>
      <UserProfileProvider />
      <GlobalLayout>
        <Component {...pageProps} />
      </GlobalLayout>
    </AuthProvider>
  )
}

export default MyApp
```
*Ignore the GlobalLayout component. It's a UI focused component that ensures each page has the same header and footer*

Now our bootstrapping component is in place and we can start making it do the thing with the stuff.

#### Wiring it up
Our bootstrapping component is going to be fetching user profile information whenever there is a logged-in user.

Based on that last sentence, we know that we _only_ want this to run _when_ we have a logged-in user. In this case, we have a currentUser hook available from our `AuthProvider` that will allow us to check for that.

Jumping back to our `UserProfileProvider`, we're going to import `firebase` and `useAuth`, as those are two things we'll need to make this work.

```javascript
'store/UseProfileProvider.js'

import { useEffect } from 'react'
import { useAuth } from 'store/AuthContext'
import 'firebase/firestore'

const UserProfileProvider = ({ children }) => {
  const { currentUser } = useAuth()

  useEffect(() => {
    
  }, [])

  return <>{children}</>
}

export default UserProfileProvider
```
These imports have more going on in them, but at a basic level- `useAuth` is allowing us to check our AuthProvider for a currentUser, and `firebase/firestore` is providing the ability to call/read from our Firebase firestore.

From this point, we're going to move into our `useEffect` and write some logic for fetching the data we need.

The first thing we're going to do is write a function that will call firebase and return the profile info of our logged-in user.

*One thing to note here is that we have a unique id of our current user available from the AuthProvider. We'll use that to make sure we're getting data from the right profile.*

```javascript
'store/UsesProfileProvider'

useEffect(() => {
    async function fetchUserProfile() {
      const userProfileInfo = await firebase.firestore()
        .collection('users')
        .doc(currentUser.userUid)
        .get()
    }
}, [])
```
What this function is doing is calling firestore and saying "hey, I need the data from the 'users' collection where the name of the document is matching this `userUid`".

*Another thing worth noting is that Firebase is promise-based, so you'll either be using async/await or .then() to resolve your promises*

We're not quite done yet!

This code will error if it were called without a currentUser. It would error because it would be asking Firestore for data based on this `userUid`, but if there's no user then there's no `userUid`- so Firestore would come back with an error essentially saying "you gave me nothing, I can't work with that."

The fix here is to wrap where we call this function inside of our useEffect in an if statement. It would look something like this.

```javascript
'store/UsesProfileProvider'

useEffect(() => {
    async function fetchUserProfile() {
      const userProfileInfo = await firebase.firestore()
        .collection('users')
        .doc(currentUser.userUid)
        .get()

      setProfileInfo(userProfileInfo.data())
    }

    if (currentUser) {
      fetchUserProfile()
    }
}, [currentUser])
```
Now our `fetchUserProfile` function will _only_ run when we have a logged-in user.

Notice that we also added currentUser to the dependency array at the end of our useEffect. This means that this code will run anytime the app boots up, whether it be from a page refresh, routing, or other scenarios, and also anytime data within our currentUser changes.

So, if our user logs in with another account, this code will run and give us fresh data to work with!

#### Ready for Use
Now that we have this bootstrapping component set up, we will always have fresh and relevant user profile info to work with!

## Summary
I was recently shown this pattern and it instantly solved a problem I'd be fighting for a bit. I needed data to be available at first load, but to also update if anything with our current user changes, and to be returned on time for our users.

Anyways, I found this pattern extremely helpful to learn and it was also fun to build.

I hope whoever finds this article has the same takeaway!

Until next time!