How to keep or resupply React Context in a Gatsby site

ぐ巨炮叔叔 提交于 2019-12-13 11:43:23

问题


I am using Gatsby as a static site generator. I use React Context API to store the information that a user is authenticated.

In development mode when I type in any URL that redirects to the 404 error page the context data is lost. When I navigate to a valid page a previously logged in user is not logged in any more.

I have never worked with React Context before so I am unsure what to do about it. Is that intended behavior? Are there ways to keep the user specific React Context? Do I have to resupply the context from the backend? Or did I just make a mistake? What is the best course of action?

I would like to keep the context in the browser somehow until the session times out. Session handling from the backend is not implemented yet.

EDIT: I just tested this with gatsby build and gatsby serve. A built gatsby site keeps the context when redirecting to 404 error page. But the context is still lost when navigating to completely different URL such as www.google.com.

Now my question is: How do I resupply the context with the login information without having the user be manually log in again? A cookie check? Sorry if I'm asking an obvious question. I never implemented sessions or cookies.

Here is my AuthContextProvider wrapper class:

import React from "react";

export const AuthContext = React.createContext();

export class AuthContextProvider extends React.Component {
  state = {
    authenticated: false,
    toggleLogin: () => {},
    userid: null,
  };

  render() {
    return (
      <AuthContext.Provider
        value={{
          authenticated: this.state.authenticated,
          userid: this.state.userid,
          toggleLogin: () => {
            const previousValueState = this.state.authenticated;
            this.setState(state => ({
              authenticated: !previousValueState,
              userid: 2,
            }));
          },
        }}
      >
        {this.props.children}
      </AuthContext.Provider>
    );
  }
}

I wrap my whole app with the Context Provider in a root layout:

export default function RootLayout({ children }) {
  return (
    <React.Fragment>
      <Helmet>
        <meta
          name="viewport"
          content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
        />
        <link rel="stylesheet"
              href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600&display=swap" />
      </Helmet>
      <GoogleReCaptchaProvider reCaptchaKey={recaptchaSiteKey}>
        <ThemeProvider theme={theme}>
          <CssBaseline />
          <AuthContextProvider>
            {children}
          </AuthContextProvider>
        </ThemeProvider>
      </GoogleReCaptchaProvider>
    </React.Fragment>
  );
}

回答1:


React Context is about providing some data to one or more child components without having to pass the data down through intermediary components. There's no built-in mechanism for persisting state between page loads, so you'll need to reach for another tool for that.

If you haven't already implemented your authentication layer, you'll want to look into how that will work. There are a number of strategies for maintaining that state, even just within using cookie-based storage. JWT (JSON Web Token) are a popular method that will let you store signed user and client-readable data in the cookie at the cost of requiring a bit more work to manage expiration/renewal and having a larger payload. Assuming that's the approach you took, you might do something like this:

import React from "react";
import jwt from "jsonwebtoken"; // Add jsonwebtoken via npm/yarn

function getCookieValue(a) {
  var b = document.cookie.match('(^|[^;]+)\\s*' + a + '\\s*=\\s*([^;]+)');
  return b ? b.pop() : '';
}

const AUTH_PUBLIC_KEY = "your JWT public key here"

export const AuthContext = React.createContext();

export class AuthContextProvider extends React.Component {
  state = {
    authenticated: false,
    userid: null,
  };

  componentDidMount() {
    jwt.verify(getCookieValue("session"), AUTH_PUBLIC_KEY, (err, session) => {
      if (!err && session.userid) {
        this.setState({ userid: session.userid, authenticated: true })
      }
    })
  }

  // Important: REMOVE THIS AFTER TESTING/DEV
  toggleLogin = () => {
    this.setState(state => ({
      authenticated: !state.authenticated,
      userid: 2,
    }));
  }

  render() {
    return (
      <AuthContext.Provider
        value={{
          ...this.state,
          toggleLogin: this.toggleLogin,
        }}
      >
        {this.props.children}
      </AuthContext.Provider>
    );
  }
}

This will parse the JWT token in the session cookie when the AuthContextProvider is mounted and update the state with the userid value stored in the JWT if one is present.

You will probably want to wrap the Gatsby App with this component, which you can do from gatsby-browser.js and gatsby-ssr.js files (create them in the root of your repo if you don't have them yet):

// gatsby-browser.js
import React from "react"
import AuthContextProvider from "components/AuthContextProvider"

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

// gatsby-ssr.js
import React from "react"
export { wrapRootElement } from "./gatsby-browser"

You will still need to handle generating the JWT token (probably from a backend that is handling authentication) and if it's not already being persisted in a cookie you can access from the browser you will need to handle creation of that cookie at the relevant point in your application lifecycle.




回答2:


I hope this helps you or others. The blog post below describes how you need to use gatsby-browser.js to wrap the root element in the provider so that it doesn't reset it on page change.

https://www.gatsbyjs.org/blog/2019-01-31-using-react-context-api-with-gatsby/



来源:https://stackoverflow.com/questions/56875219/how-to-keep-or-resupply-react-context-in-a-gatsby-site

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!