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?

Andrea Ligios
  • 49,480
  • 26
  • 114
  • 243
  • 1
    [Here](https://www.gatsbyjs.org/blog/2019-01-31-using-react-context-api-with-gatsby/) is a great blog post from the official Gatsby blog that could be useful to solve what you need. – Lionel T Sep 23 '19 at 15:48
  • 1
    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 ASAP – Andrea Ligios Sep 23 '19 at 20:25

2 Answers2

10

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.
EliteRaceElephant
  • 7,744
  • 4
  • 47
  • 62
  • 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 you – Andrea Ligios Sep 24 '19 at 07:34
  • @AndreaLigios Yes, correct. My solution was implementing json web tokens in order to recognize a recurring user as the answer you mentioned suggested. – EliteRaceElephant Sep 25 '19 at 00:31
  • this worked for me but it loads all my layouts twice which has caused some issues. – Edward Apr 13 '22 at 22:34
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
}
Andrea Ligios
  • 49,480
  • 26
  • 114
  • 243