使用 React Context 的用户 Authorisation/Authentication 出现问题。如何访问 class 组件中的变量?

Having issues with user Authorisation/Authentication with React Context. How do I access the variables in a class component?

所以我是第一次使用 React Context 和 Hooks。我遵循了 YouTube 教程并且我有用户授权和身份验证工作,但是我真的很难弄清楚如何访问活动用户以及该用户是否通过 class 组件进行身份验证。我遵循的教程仅使用了 React Hooks,并且我的项目混合使用了钩子和 class 组件。 在我下面提供的代码中,如果我们查看 NavBar class,它可以轻松检查用户是否已通过身份验证,因为这是一个挂钩组件。我希望能够为 class 组件(例如 App.js 和 Step1.js)执行此操作。在 App.js 中,我使用上下文 api 文档进行了尝试。

 const Auth = this.context;
    console.log(Auth);

然而,这只是 returns 一个空集。我是否缺少消费者标签,或者提供商标签应该在其他地方?如果有任何帮助或建议,我将不胜感激!

AuthContext.js

import React, {createContext,useState,useEffect} from 'react';
import AuthService from '../Services/AuthService';

export const AuthContext = createContext();

export default ({ children })=>{
    const [user,setUser] = useState(null);
    const [isAuthenticated,setIsAuthenticated] = useState(false);
    const [isLoaded,setIsLoaded] = useState(true);

    useEffect(()=>{
        AuthService.isAuthenticated().then(data =>{
            setUser(data.user);
            setIsAuthenticated(data.isAuthenticated);
            setIsLoaded(true);
        });
    },[]);

    return (
        <div>
            {!isLoaded ? <h1>Loading</h1> : 
            <AuthContext.Provider value={{user,setUser,isAuthenticated,setIsAuthenticated}}>
                { children }
            </AuthContext.Provider>}
        </div>
    )
            }

AuthService.js

export default {
  login: user => {
    return fetch("/api/login", {
      method: "post",
      body: JSON.stringify(user),
      headers: {
        "Content-Type": "application/json"
      }
    })
      .then(res => res.json())
      .then(data => data);
  },

  register: user => {
    return fetch("/api/register2", {
      method: "post",
      body: JSON.stringify(user),
      headers: {
        "Content-Type": "application/json"
      }
    })
      .then(res => res.json())
      .then(data => data);
  },

  logout: () => {
    return fetch("/api/logout")
      .then(res => res.json())
      .then(data => data);
  },

  isAuthenticated: () => {
    return fetch("api/authenticated").then(res => {
      if (res.status != 401) return res.json().then(data => data);
      else return { isAuthenticated: false, user: { username: "", role: "" } };
    });
  }
};

App.js

import React, { Component, useContext } from "react";
import { getFromStorage, setInStorage } from "../../utils/storage";
import Navbar from "../NavBar";
import Header from "../Header/Header";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  withRouter,
  Redirect,
  useRouteMatch,
  useParams
} from "react-router-dom";
import Home from "../Home/Home";
import SideBar from "../SideBar";
import Step1 from "../SafetyPlan/Step1";
import AllSteps from "../SafetyPlan/AllSteps";
import PatientHome from "../Patient/PatientHome";
import HeaderOut from "../Header/HeaderOut";
import AddPatient from "../Patient/AddPatient";
import Step2 from "../SafetyPlan/Step2"
import SignUp from "../Register";
import Login from "../Login";
import PrivateRoute from "../HOCs/PrivateRoute";
import UnPrivateRoute from "../HOCs/PrivateRoute";
import AuthContext from '../..//Context/AuthContext'


//I want to be able to access isAuthenticated and user within this class
//I have not sure how to access them. I understand that It is possible but I am struggling to get it to work.
//The NavBar class makes use these hooks in the way I want to. It is a good example I want to look at.

class App extends React.Component {

  //Equivalent to useContext() for functional components
  static contextType = AuthContext;
  
  constructor(props) {
    super(props);
    this.state = {
      token: ""
    };
  }

 
  render() {    
    //Access context using this.context (similar to this.props)
    const Auth = this.context;
    console.log(Auth);

    return (
      // This is your Context Provider (should wrap all other components that require context)
      <AuthContext> 
        {/* The way you designed it, all children will be Context Consumers (see AuthContext.js) */}
      <Router>
        <div>
          <Header />
          <SideBar></SideBar>
          <Switch>
            <Route exact path="/" component={SignUp} />
            <Route exact path="/Step1" component={Step1} />
            <Route exact path="/Step2" component={Step2} />
            <Route exact path="/AllSteps" component={AllSteps} />
            <Route exact path="/AddPatient" component={AddPatient} />
            <Route exact path="/Register" component={SignUp} />
            <Route exact path="/Login" component={Login} />
            <Route path="/PatientHome/:id" component={PatientHome} />
          </Switch>
          )
        </div>
      </Router>
      </AuthContext>
    );
  }
}
export default App;

NavBar.js

import React, {useContext} from 'react';
import {Link} from 'react-router-dom';
import AuthService from '../Services/AuthService';
import { AuthContext } from '../Context/AuthContext';



const Navbar = props =>{
    const {isAuthenticated,user,setIsAuthenticated,setUser} = useContext(AuthContext);
    console.log(isAuthenticated);
    console.log(user);
    const onClickLogoutHandler = ()=>{
        AuthService.logout().then(data=>{
            if(data.success){
                setUser(data.user);
                setIsAuthenticated(false);
            }
        });
    }

    const unauthenticatedNavBar = ()=>{
        return (
            <>
                <Link to="/">
                    <li className="nav-item nav-link">
                        Home
                    </li>
                </Link>  
                <Link to="/login">
                    <li className="nav-item nav-link">
                        Login
                    </li>
                </Link>  
                <Link to="/register">
                    <li className="nav-item nav-link">
                        Register
                    </li>
                </Link>  
            </>
        )
    }

    const authenticatedNavBar = ()=>{
        return(
            <>
                <Link to="/">
                    <li className="nav-item nav-link">
                        Home
                    </li>
                </Link> 
                <Link to="/todos">
                    <li className="nav-item nav-link">
                        Todos
                    </li>
                </Link> 
                {
                    user.role === "admin" ? 
                    <Link to="/admin">
                        <li className="nav-item nav-link">
                            Admin
                        </li>
                    </Link> : null
                }  
                <button type="button" 
                        className="btn btn-link nav-item nav-link" 
                        onClick={onClickLogoutHandler}>Logout</button>
            </>
        )
    }
    return(
        <nav className="navbar navbar-expand-lg navbar-light bg-light">
            <Link to="/">
                <div className="navbar-brand">SafePlan View</div>
            </Link>
            <div className="collapse navbar-collapse" id="navbarText">
                <ul className="navbar-nav mr-auto">
                    { !isAuthenticated ? unauthenticatedNavBar() : authenticatedNavBar()}
                </ul>
            </div>
        </nav>
    )
}

export default Navbar;

Step1.js

import React from "react";
import styled from "styled-components";
import axios from "axios";
import { getFromStorage, setInStorage } from "../../utils/storage";
import AuthContext from './/..//..//Context/AuthContext'
import {
  BrowserRouter as Router,
  Route,
  Link,
  withRouter
} from "react-router-dom";
import {
  Form,
  Button,
  FormGroup,
  FormControl,
  ControlLabel
} from "react-bootstrap";
import Home from "../Home/Home";
const GridWrapper = styled.div`
  p {
    font-size: 18px;
  }
  h3 {
    text-decoration: underline;
  }
  margin-top: 4em;
  margin-left: 28em;
  margin-right: 1em;
`;

class Step1 extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      token: "",
      session: 0,
      date: "",
      versions: [],
      warnings: [],
      emailOfEditor: "Hello",
      purpose: [],
      privateBenefit: "wewewe",
      achievablePlan: "wewewewe",
      purposeEvidence: "ewewe",
      achievablePlanEvidence: "wewe",
      privateBenefitEvidence: "wewewew",
      charityName: "",
      time: ""
    };
  }
  static contextType = AuthContext;

  updateInput = e => {
    this.setState({
      [e.target.name]: e.target.value
    });
  };

  getPatientId = (event, { value }) => {
    console.log(value);
    let bird_name = event.target.textContent;
    console.log(bird_name);
  };
  componentDidMount() {
    const obj = getFromStorage("the_main_app");
    if (obj && obj.token) {
      const { token } = obj; // Verify token
      fetch("/api/account/verify?token=" + token)
        .then(res => res.json())
        .then(json => {
          if (json.success) {
            this.setState({
              token,
              isLoading: false
            });
          } else {
            this.setState({
              isLoading: false
            });
          }
        });
    } else {
      this.setState({
        isLoading: false
      });
    }
    axios.get("/api/getStep1").then(res => {
      const warnings = res.data
      this.setState({ warnings });
      console.log(warnings);
    });
  }

  generateNewSession = e => {
    var that = this;
    let sessionNum = Math.floor(100000 + Math.random() * 900000);
    this.setState({ session: sessionNum });
    this.setState({ date: new Date().toLocaleString() });
    console.log(this.state.session);
    console.log(this.state.date);
    const { warning } = this.state;

    // Post request to backend
    fetch("/api/updateSession", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        warning: warning
      })
    })
      .then(res => res.json())
      .then(json => {
        console.log("json", json);
      });
  };

  render() {
    const { versions } = this.state;
    const { token } = this.state;
    
      return (
        <GridWrapper>
          <div className="row">
            <div className="col-md-10">
              <div className="card" style={{ backgroundColor: "#f0923c" }}>
                <div className="card">
                  <div className="card-body" style={{ paddingTop: "10px" }}>
                    <h3 style={{ fontWeight: "" }}>
                      {" "}
                      Step 1: Warning Signs (thoughts, images, mood, situation,
                      behaviour) that a crisis may be developing
                    </h3>

                    <ul style={{ fontSize: "18px" }}>
                      {this.state.warnings.map(warning => (
                        <li>{warning.signDesc}</li>
                      ))}
                    </ul>
                    <br></br>
                    <br></br>
                    <br></br>
                    <br></br>
                    <br></br>
                    <br></br>
                  </div>
                </div>
              </div>
            </div>
            <div className="col-md-2">
              <Link to>
                <div style={{ color: "#f0923c" }}>Next Step</div>
              </Link>
            </div>
          </div>
        </GridWrapper>
      );
    }

  
  
}
export default Step1;

基本上当用户登录时,您应该返回一个安全令牌密钥,最常见的是 JWT(JSON Web 令牌),您应该将此令牌存储在 localstoragecachestorage 并在您的组件中检索它并查看用户是否已通过身份验证。如果您没有正确实施 JWT,那么您现在可以使用一个布尔变量并检查用户是否已登录。

你应该保护路由设置。我附上了用于在本地存储中存储值的代码段。

登录组件

  if(postLogin(user)){
    localStorage.setItem('token',true);
}

创建PrivateRoute,然后在PrivateRoute中, 检查用户登录是否存在,并根据它重定向。

import {Redirect} from "react-router-dom";
import React from "react";

const Route = require("react-router-dom").Route;
export const PrivateRoute = ({component: Component, ...rest}) => {
    return (<Route {...rest} render={props => (
        localStorage.getItem('token') ? (
            <Component {...props}/>
        ) : (
            <Redirect push to={{
                pathname: '/login',   //whatever your component is
                from: props.location
            }}/>
        )
    )}/>)
}; 

在你的App.js

import {PrivateRoute} from './PrivateRoute'
.
.
.
 <PrivateRoute exact path={"/home/posts"} component={Posts} />  //User cannot access this component without being logged in.

Note: Make sure to clear the localStorage or cacheStorage through user logout.