Where to put Context Provider in Gatsby?
Asked Answered
D

2

12

I need to run some functions (eg. Office UI Fabric React's initializeIcons()) and AXIOS call (eg. retrieve the logged-in user with Context API) only the first time the site is hit, then store the retrieved values in the React Context and make it available to the whole application.

Gatsby is wrapping my pages' content in a Layout, like:

const IndexPage = () =>
<Layout>
   Body of Index Page...
</Layout>
const AnotherPage = () =>    
<Layout>
   Body of Another Page...
</Layout>

with Layout being like:

const Layout = ({ children }) =>
<>
    <Header /> 
    <main>{children}</main>
    <Footer />
</>

I know where I can NOT put my Context:

  • around the pages (or it will be executed everytime the page is hit, and also not available to the other pages):

    const IndexPage = () =>
    <MyContextProvider>
        <Layout>
           Context Available here
        </Layout>
    </MyContextProvider>
    
    const AnotherPage = () =>    
    <Layout>
        Context NOT available here
    </Layout>
    
  • in the Layout (or it will be executed every time):

    const Layout = ({ children }) =>
    <MyContextProvider>
        <Header /> 
        <main>{children}</main>
        <Footer />
    </MyContextProvider>
    

I suppose I need a root <app> object to surround with my Context Provider, but what's a clean way to achieve that with Gatsby?

Where should I put my Context Provider?

Duong answered 23/9, 2019 at 13:17 Comment(2)
Here is a great blog post from the official Gatsby blog that could be useful to solve what you need.Tasman
Thank you @LionelT. That's basically what I've tried, with the exclusion of localStorage (which is fine for saving a theme, but not for the logged in user AFAIK). I can use the sessionStorage (but then I'd need to deal with two different users logging from the same pc subsequently and without closing the browser...), I just hoped there's a way to "store" the Axios call's result and avoid executing it again - without resorting to some HTML5 storage... thanks anyway, I'll explore it ASAPDuong
J
11

You define a root layout. In contrast to the normal layout there are no "visible" page elements defined but hidden stuff you need on every page like ContextProviders, React Helmet, themes, etc:

RootLayout.jsx:

export default function RootLayout({ children }) {
  return (
    <>
      <Helmet />
        <ThemeProvider theme={theme}>
          <CssBaseline />
          <ContextProvider>
            {children}
          </ContextProvider>
        </ThemeProvider>
    </>
  );
}

Gatsby calls this root layout implicitly via gatsby-browser.js and gatsby-ssr.js and applies it to each of your pages. Those two identical lines of code are all you need for Gatsby to handle the rest for you.

gatsby-browser.js:

export const wrapRootElement = ({ element }) => <RootLayout>{element}</RootLayout>;

gatsby-ssr.js:

export const wrapRootElement = ({ element }) => <RootLayout>{element}</RootLayout>;

Summary:

  • You put your Context Provider in a root layout.

References:

  • I asked related questions here and here. The code I provided in this answer is the solution to your and my question. It is good coding practice adapted from frameworks such as React Redux to wrap your whole app with the Context Provider if your whole app needs this information.
  • The blog post @Lionel T mentioned.
Jena answered 24/9, 2019 at 3:31 Comment(3)
OK, the sentence in the answer to your other question (There's no built-in mechanism for persisting state between page loads, so you'll need to reach for another tool for that) makes the point that I'd need to "manually" implement some cookie or sessionStorage solution for the Auth part. At least now I'm sure there's nothing out of the box for that in Gatsby, thank youDuong
@AndreaLigios Yes, correct. My solution was implementing json web tokens in order to recognize a recurring user as the answer you mentioned suggested.Jena
this worked for me but it loads all my layouts twice which has caused some issues.Romilda
D
0

For completeness, the APIs Gatsby provides (through gatsby-browser.js) to run a function only once are:

onClientEntry

export const onClientEntry = () => {
    console.log("Browser started!")
    // Put here run-only-once functions 
}

onInitialClientRender

export const onInitialClientRender = () => {
    console.log("First rendering completed!")
    // Put here run-only-once functions which need rendered output
}
Duong answered 24/9, 2019 at 8:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.