Yingqi
Yingqi

Reputation: 1515

why my context doesn't update when the context value updates?

so I am using React's context API in my Gatsby app(which is written in React basically) to deal with user authentication. I have two components that use that context: dashboard and navBar. When I try to log in and log out, my navBar will behave differently according to my userContext, but my dashboard won't respond. Is it something related to the structure, like navBar is the direct "children" to layout, but dashboard is not? I assume not though, after all, that's why I use contextAPI then just pass a normal prop.

Here are the codes:

//layout.js
import React, { useContext, useState, createContext } from "react"
import Navbar from "../components/navBar"
import {monitorAuth} from "../firebase/firebaseService"

export const UserStateContext = createContext(null)
export const SetUserContext = createContext()

const Layout = ({ children }) => {
  const [user, setUser] = useState()
  console.log(user)

  monitorAuth(setUser)// everytime a layout component renders, it will grab a user if it is logged inthen setUser, then I will use it in the context

  return (
    <>
      <UserStateContext.Provider value={user}>
        <SetUserContext.Provider value={setUser}>
          <div>
            <SEO />
            <Navbar />
            <main>{children}</main>
          </div>
        </SetUserContext.Provider >
      </UserStateContext.Provider>
    </>
  )
}


export default Layout

import React, { useState, useContext } from "react"
import AppBar from "@material-ui/core/AppBar"
import { signOut } from "../firebase/firebaseService"
import {UserStateContext} from "./layout"

export default function NavBar() {
  const user = useContext(UserStateContext)
  console.log(user) // when I log in/ log out, it will console.log the right user status, user/null

  const renderMenu = () => {
    return (
    <>
      {user? (
      <>
      <Button onClick={signOut}>Sign Out</Button>
      <Button>My profile</Button> 
      </>)
     :<Button>Sign In</Button> }
    </>
    )
  }

  return (
    <AppBar position="static" className={classes.root}>
        ...
        {renderMenu()}
        ...
  </AppBar>
  )
}

//dashboard.js
import React, { useContext } from 'react'
import Layout from '../components/layout'
import LoggedIn from '../components/dashboard/loggedIn'
import NotLoggedIn from '../components/dashboard/notLoggedIn'
import {UserStateContext} from "../components/layout"


const Dashboard = props => {
    console.log("within dashboard")
    const user = useContext(UserStateContext)
    console.log(user)

    const renderDashboard = () =>{
       return (
         <>
         {user? <LoggedIn /> : <NotLoggedIn />}
         </>
         
       )
    }

    return(
      <Layout>
        {renderDashboard()}
      </Layout>
    )
}

export default Dashboard

One more clue, I console.log user in all three components and when I refresh the page:

within dashboard
dashboard.js:17 null
layout.js:15 undefined
navBar.jsx:54 undefined
layout.js:15 [user...]
navBar.jsx:54 [user...]
layout.js:15 [user...]

That means, at first, user is not set yet, so all three components log the user as undefined, but later, layout detect the user and then updates it, so navbarknows too, but dashboard doesn't. Is it something about re-render? Thanks!

Upvotes: 1

Views: 8729

Answers (1)

paulshen
paulshen

Reputation: 384

The reason it's not working is because your <Dashboard> component is not a child of the context provider. If you use React devtools, you'll see the component tree looks like

<Dashboard>
  <Layout>
    <UserStateContext.Provider>
      <SetUserContext.Provider>
        ...
      </SetUserContext.Provider> 
    </UserStateContext.Provider> 
  </Layout>
</Dashboard>

When the context value changes, it looks for components in its subtree that useContext. However, Dashboard is not a child, it's the parent!

If you want to follow this pattern, a solution may be to create a parent component of Dashboard and put the context there.

Upvotes: 8

Related Questions