admin管理员组

文章数量:1318570

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 ponents 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 "../ponents/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 ponent 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 '../ponents/layout'
import LoggedIn from '../ponents/dashboard/loggedIn'
import NotLoggedIn from '../ponents/dashboard/notLoggedIn'
import {UserStateContext} from "../ponents/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 ponents 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 ponents 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!

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 ponents 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 "../ponents/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 ponent 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 '../ponents/layout'
import LoggedIn from '../ponents/dashboard/loggedIn'
import NotLoggedIn from '../ponents/dashboard/notLoggedIn'
import {UserStateContext} from "../ponents/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 ponents 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 ponents 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!

Share Improve this question edited Aug 5, 2020 at 13:28 Ferran Buireu 29.3k7 gold badges46 silver badges72 bronze badges asked Aug 5, 2020 at 12:13 YingqiYingqi 1,5354 gold badges16 silver badges28 bronze badges 8
  • 1 Why do you use two contexts for this? You can bine them. Also i never see u re-use SetUserContext you need to call setUser to trigger a render – Stutje Commented Aug 5, 2020 at 12:32
  • 1 I gave it a try to bine them and it should work, if you follow my example :) – Stutje Commented Aug 5, 2020 at 12:43
  • You have to call your setter in order to update your context state. – JULIEN PICARD Commented Aug 5, 2020 at 13:31
  • @Stutje Thank you! The reason I use two different contexts is that it prevents from too much re-render. But thanks for doing the code-pen :) It is just part of the codes, I would use the SetUserContext somewhere else, and setUser is used in monitorAuth to set the user's value – Yingqi Commented Aug 5, 2020 at 22:38
  • @JULIENPICARD I did. It is just part of the codes, I would use the SetUserContext somewhere else, and setUser is used in monitorAuth to set the user's value – Yingqi Commented Aug 5, 2020 at 22:40
 |  Show 3 more ments

1 Answer 1

Reset to default 8

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

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

When the context value changes, it looks for ponents 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 ponent of Dashboard and put the context there.

本文标签: javascriptwhy my context doesn39t update when the context value updatesStack Overflow