使用 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 令牌),您应该将此令牌存储在 localstorage
或 cachestorage
并在您的组件中检索它并查看用户是否已通过身份验证。如果您没有正确实施 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.
所以我是第一次使用 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 令牌),您应该将此令牌存储在 localstorage
或 cachestorage
并在您的组件中检索它并查看用户是否已通过身份验证。如果您没有正确实施 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.