2

I'm using the context api in a Gatsby setup to keep track of a state called userIsLoggedIn. I'm using Firebase for authentication.

This is my context file:

import { createContext } from "react"

export const AppContext = createContext(null)

This is my AppWrapper component:

import React, { useState, useEffect } from "react"
import firebase from "../../config/firebase"
import { AppContext } from "../../context/AppContext"

const AppWrapper = ({ children }: any) => {
  const [userIsLoggedIn, setUserIsLoggedIn] = useState(false)

  const authListener = () => {
    firebase.auth().onAuthStateChanged(user => {
      if (user && user.emailVerified) {
        setUserIsLoggedIn(true)
      } else {
        setUserIsLoggedIn(false)
      }
    })
  }

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

  return (
    <>
      <AppContext.Provider
        value={{
          userIsLoggedIn,
        }}
      >
        <main>{children}</main>
      </AppContext.Provider>
    </>
  )
}

export default AppWrapper

This is my index page where I want to keep track if the user is logged in so I can show/hide certain content:

import React, { useContext } from "react"
import { AppContext } from "../context/AppContext"

const IndexPage = () => {
  const app = useContext(AppContext)

  console.log("app", app)

  return (
    <>
      {app && app.userIsLoggedIn && (
        <>
          <h1>Hello dearest user</h1>
          <p>Welcome to your page.</p>
        </>
      )}
    </>
  )
}

export default IndexPage

The outcome of my console.log inside the my IndexPage component is the following when I first load the page or whenever the page is reloaded:

app {userIsLoggedIn: false}
app {userIsLoggedIn: true}

This means my page is re-rendering and my content is flickering between content which is hidden/shown when a user is logged in. Is there a way to avoid this and make the state more instant? I'm open for any suggestions :)

Sebastiaan
  • 105
  • 2
  • 10
  • Try to change the *useEffect* `authListener` logic like [this](https://stackoverflow.com/a/55370253/8254484) – awran5 Apr 19 '20 at 01:20

1 Answers1

0

Okay so I found out what helps my specific case. Instead of using the context api to keep an app state (which will be reset to it's default value when reloaded, hence the flickering between states) I use localStorage to save if a user is logged in in combination with my authListener function.

This is the auth util I added:

// Firebase
import firebase from "../config/firebase"

export const isBrowser = () => typeof window !== "undefined"

export const getUser = () => {
  if (isBrowser()) {
    const user = window.localStorage.getItem("user")
    if (user !== null) {
      return JSON.parse(user)
    } else {
      return ""
    }
  }
}

export const setUser = (email: string | null) =>
  isBrowser() && window.localStorage.setItem("user", JSON.stringify(email))

export const isLoggedIn = () => {
  const user = getUser()
  return !!user
}

export const logout = () => {
  return new Promise(resolve => {
    firebase
      .auth()
      .signOut()
      .then(() => {
        setUser("")
        resolve()
      })
  })
}

and inside my AppWrapper my authListener function now looks like this:

import { setUser, logout } from "../../utils/auth"

const authListener = () => {
    firebase.auth().onAuthStateChanged(user => {
      if (user && user.emailVerified) {
        setUser(user.email)
      } else {
        logout()
      }
    })
  }

  useEffect(() => {
    authListener()
  })

Anywhere in my app I can use the isLoggedIn() util to check if the user is actually logged in without having the flickering content.

If I manually delete or alter the localStorage user this will instantly be refreshed by the authListener function when anything changes in the app.

Sebastiaan
  • 105
  • 2
  • 10