I've a question regarding Context API:
I have a page which consists of Layout, which is splitted in Sidebar, Navigation, Footer... .
I've set up a Context to show and hide some stuff (in my case a share area).
I have a file within /pages folder and wanna consume that context which was clicked in the layout area, but it is not changing in the pages.
Here is my example: It simply toggles true false if somewhere in Layout/Footer/Navigation
麓/context/ShareContext.js麓
import React, { createContext } from "react"
const defaultState = {
collapsedShare: false,
toggleShare: () => {},
}
const ShareContext = createContext(defaultState)
const ShareContextProvider = props => {
const [collapsedShare, setCollapsedShare] = useState(false)
const toggleShare = () => setCollapsedShare(!collapsedShare)
return (
<ShareContext.Provider value={{ collapsedShare, toggleShare: toggleShare }}>
{props.children}
</ShareContext.Provider>
)
}
export { ShareContextProvider, ShareContext }
/src/layout
import React from "react"
import { ShareContextProvider } from "../context/ShareContext"
const Layout = props => {
const [collapsed, setCollapsed] = useState(true)
const toggleNav = () => setCollapsed(!collapsed)
return <ShareContextProvider>
<Sidebar/>
<Button onClick={toggleShare} > Button Text </Button>
<Footer/>
</ShareContextProvider>
/pages/home.js (where I wanna consume)
import React, { useContext } from "react"
import Layout from "../components/layout"
import { ShareContext } from "../context/ShareContext"
const IndexPage = props => {
const { collapsedShare, toggleShare } = useContext(ShareContext)
console.log(collapsedShare)
return (
<Layout>
<div id="wrapper">
Some content and my context to consume
{String(collapsedShare)} <<<<<<----------- is always "false" (never changes)
</div>
</Layout>
)
}
Do I have a wrong understanding how Gatsby works or is this not possible to do with Gatsby
I've made this work so it's definitely possible. Can you put together a repo so I can better see what you're attempting?
@herecydev
I've made this work so it's definitely possible. Can you put together a repo so I can better see what you're attempting?
I made here a repo to point out this problem. Very simplified, but shows the issue:
Hey your example isn't very simple to follow, but a quick glance shows the component tree looks like:

So the context provider is rendered at the wrong location for you to consume it in the page
Yea, I think there is a lot happening under the hood that makes this difficult to explain/visualize, but here is your sandbox forked with it fixed by moving the Context consumer to a _child_ of the Index page instead of in the Index page itself:
https://codesandbox.io/s/admiring-hooks-lzxb4?file=/src/pages/index.js
The relevant part:
import React, { useContext } from "react"
import Layout from "../components/layout"
import { ShareContext } from "../context/ShareContext"
// Move this to it's own component consuming context.
const IndexContent = () => {
const { collapsedShare, toggleShare } = useContext(ShareContext)
return (
<>
<p>collapsedShare in index page is {String(collapsedShare)} </p>
<button onClick={toggleShare}>
I am in the page: Click me to change
</button>
</>
)
}
const IndexPage = () => {
// Consuming the context here lifts it above the Layout, and therefore above
// the context provider.
return (
<Layout>
<h3>
I am the page where a context should update, when it's set outside
</h3>
<IndexContent />
</Layout>
)
}
export default IndexPage
Yer because previously you were using the context that was declared at the "index page level", where layout is now responsible for rendering the IndexContent component. This will then have a context that's "somewhere beneath the layout".
It's really hard to articulate, but hope that helps?
Dan summarised it pretty well (I also have no way of articulating it better :) ).
To fix the problem the Provider should be above consumers and you can do this by using https://www.gatsbyjs.org/packages/gatsby-plugin-layout/ and putting Provider there, then you could use it in your page components directly. Alternatively you could use wrapRootElement yourself to do this (just keep in mind you need same thing for gatsby-ssr and gatsby-browser file as mentioned in the note of the doc I linked to). (but the plugin I linked to just abstract similar API and generally is nicer to use if you don't need additional control there)
Interesting! For my point of view is this "magic" of Gatsby (I'm Gatsby noob).
Very nice thank you, have to read through. I will close this now. Thanks again.
Most helpful comment
Dan summarised it pretty well (I also have no way of articulating it better :) ).
To fix the problem the Provider should be above consumers and you can do this by using https://www.gatsbyjs.org/packages/gatsby-plugin-layout/ and putting Provider there, then you could use it in your page components directly. Alternatively you could use
wrapRootElementyourself to do this (just keep in mind you need same thing forgatsby-ssrandgatsby-browserfile as mentioned in the note of the doc I linked to). (but the plugin I linked to just abstract similar API and generally is nicer to use if you don't need additional control there)