React - 注销组件问题
Posted
技术标签:
【中文标题】React - 注销组件问题【英文标题】:React - Logout component issue 【发布时间】:2021-11-13 14:38:10 【问题描述】:新的反应和处理具有不同客户类型的优惠券项目。
基本上我的登录组件工作正常,我正在使用Redux
来控制操作。
AuthComponent:
export class AuthAppState
public user: NewUserModel = null;
public constructor()
const storedUser = JSON.parse(localStorage.getItem('user')!);
if(storedUser)
this.user = storedUser;
// Step 2 - Define ActionType using enum for all required operations
export enum AuthActionType
Register = "Register",
Login = "Login",
Logout = "Logout"
// Step 3 - Define Action Interface to describe actionAction & payload if needed
export interface AuthAction
type: AuthActionType;
payload?: any; // ? for logout
// Step 4 - Export Action Creators functions that gets payload and return relevant Action
export function registerAction(user: UserModel): AuthAction
return type: AuthActionType.Register,payload:user ;
export function loginAction(user: NewUserModel): AuthAction
return type: AuthActionType.Login ,payload:user;
export function logoutAction(): AuthAction
return type: AuthActionType.Logout;
// Step 5 - Reducer function perform the required action
export function authReducer(currentState: AuthAppState = new AuthAppState(),
action:AuthAction): AuthAppState
// const newState = new CatsAppState();
// newState.cats = currentState.cats;
const newState = ...currentState //Spread Operator
switch(action.type)
case AuthActionType.Register: //Payload is registered user from backend
newState.user = action.payload;
localStorage.setItem("user",JSON.stringify(newState.user)); // Saving in the session storage (won't be deleted)
break;
case AuthActionType.Login://Payload is logged i user from backend
newState.user = action.payload;
localStorage.setItem("user",JSON.stringify(newState.user)); // Saving in the session storage (won't be deleted)
break;
case AuthActionType.Logout: // No payload
newState.user = null;
localStorage.removeItem("user");
break;
return newState;
这是我的登录组件,工作正常。
登录组件::
function Login(): JSX.Element
const history = useHistory();
const [loginType, setLoginType] = useState("Administrator"); // Save Login Type HERE
const register, handleSubmit, formState: errors = useForm<CredentialsModel>();
async function send(credentials: CredentialsModel)
console.log(credentials);
let response = null;
try
switch (loginType)
case 'Customer':
response = await axios.post<NewUserModel>(globals.urls.customer + "login", credentials);
store.dispatch(loginAction(response.data));
notify.success(SccMsg.LOGIN_SUCCESS);
break;
case 'Company':
response = await axios.post<NewUserModel>(globals.urls.company + "login", credentials);
store.dispatch(loginAction(response.data));
notify.success(SccMsg.LOGIN_SUCCESS);
break;
case 'Administrator':
response = await axios.post<NewUserModel>(globals.urls.admin + "login", credentials);
store.dispatch(loginAction(response.data));
notify.success(SccMsg.LOGIN_SUCCESS);
break;
console.log(response.data);
history.push(loginType);
catch (err)
notify.error(err);
return (
<div className="base-container " >
<form onSubmit=handleSubmit(send)>
<div className="header" ></div>
<div className="content">
<div className="image">
<img src=loginImage />
</div>
<div className="form">
<div className="form-group">
<label htmlFor="username">Email</label>
<input type="email" placeholder="???? email"
...register("email", required: true, pattern: /^\S+@\S+$/i )
/>
errors.email?.type === 'required' && <span>Enter a valid email address</span>
</div>
<div className="form-group">
<label htmlFor="username">Password</label>
<input type="password" placeholder="???? password"
...register("password",
required: true,
minLength: 4,
maxLength: 12,
)
/>
errors.password?.type === 'required' && <span>You must enter a password</span>
errors.password?.type === 'minLength' && <span>Password too short</span>
errors.password?.type === 'maxLength' && <span>Password too long</span>
</div>
<div className="form-group">
<div className="loginas">Login as:</div>
<div className="">
<select onChange=(e) => setLoginType(e.target.value) name='clientType' >
<option value="Administrator">ADMINISTRATOR</option>
<option value="Customer">CUSTOMER</option>
<option value="Company">COMPANY</option>
</select>
</div>
</div>
</div>
</div>
<div className="footer">
<Button type="submit" className=".btn" buttonStyle='btn--outline'>Login</Button>
</div>
<div className="register">
<p >
Don't have an account?
<br />
Please click here to <NavLink to="/Register"> REGISTER</NavLink>
</p>
</div>
</form>
</div>
);
export default Login;
一旦我使用正确的凭据和正确的客户端类型,用户将使用 AuthoMenu 组件保存到LocalStorage
,并带有来自后端的令牌:
interface AuthMenuState
user: NewUserModel;
class AuthMenu extends Component<, AuthMenuState>
private unsubscribe: Unsubscribe;
public constructor(props: )
super(props);
this.state =
user: store.getState().authState.user,
;
public componentDidMount(): void
store.subscribe(() =>
this.setState( user: store.getState().authState.user );
);
public componentWillUnmount(): void
this.unsubscribe();
public render(): JSX.Element
return (
<div className="AuthMenu">
this.state.user && (
<>
<span>
Hello this.state.user.name + " "
</span>
<span> | </span>
<NavLink to="/logout" className="normal" activeClassName="active">Logout</NavLink>
</>
)
!this.state.user && (
<>
<span>Hello Guest</span>
<span> | </span>
<NavLink to="/login" className="normal" activeClassName="active">Login</NavLink>
<span> | </span>
<NavLink to="/register" className="normal" activeClassName="active">Register</NavLink>
</>
)
</div>
);
到目前为止,一切似乎都运行良好。
我也使用 react Hooks
和 Functional Components
。
我不确定在注销组件上删除令牌的正确方法。
在我的后端,我使用 jwt 作为 intellij 的安全性。
注销组件:
function Logout(): JSX.Element
const history = useHistory();
async function send( )
let token = localStorage.getItem('token');
const response = await axios.delete<String>("http://localhost:8080/customer/logout");
notify.success(SccMsg.LOGOUT_SUCCESS);
store.dispatch(logoutAction());
history.push("/home");
useEffect(() =>
);
return (
<></>
);
我的注销控制器非常简单。
后端控制器:
@DeleteMapping("/logout")
public ResponseEntity<?> logout(@RequestHeader("Authorization") String token)
jwtUtils.removeToken(token);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
InterceptorAxios 组件: const tokenAxios = axios.create();
tokenAxios.interceptors.request.use(request =>
request.headers =
"authorization": store.getState().authState.user?.token
;
return request;
);
导出默认tokenAxios;
希望我的问题很清楚。
我只需要在注销后清除LocalStorage
。
编辑:
使用 Inspect 时出现此错误:
xhr.js:177 DELETE http://localhost:8080/customer/logout 401
dispatchXhrRequest @ xhr.js:177
xhrAdapter @ xhr.js:13
dispatchRequest @ dispatchRequest.js:52
Promise.then (async)
request @ Axios.js:61
Axios.<computed> @ Axios.js:76
wrap @ bind.js:9
send @ Logout.tsx:20
(anonymous) @ Logout.tsx:31
invokePassiveEffectCreate @ react-dom.development.js:23487
callCallback @ react-dom.development.js:3945
invokeGuardedCallbackDev @ react-dom.development.js:3994
invokeGuardedCallback @ react-dom.development.js:4056
flushPassiveEffectsImpl @ react-dom.development.js:23574
unstable_runWithPriority @ scheduler.development.js:468
runWithPriority$1 @ react-dom.development.js:11276
flushPassiveEffects @ react-dom.development.js:23447
(anonymous) @ react-dom.development.js:23324
workLoop @ scheduler.development.js:417
flushWork @ scheduler.development.js:390
performWorkUntilDeadline @ scheduler.development.js:157
createError.js:16 Uncaught (in promise) Error: Request failed with status code 401
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:62)
我认为有授权问题。
【问题讨论】:
【参考方案1】:-
注意你在后端提到了
Authorization
标头作为令牌,而不是authorization
。如需进一步验证,请检查您的授权中间件中用于验证后端令牌的 header
密钥。
在您的InterceptorAxios
中,您需要传递相同的header
密钥以获得带有精确标点符号的令牌。
对于 JWT 令牌,通常令牌具有特定格式,即 "Bearer <token>"
试试这个
const tokenAxios = axios.create();
tokenAxios.interceptors.request.use(request =>
request.headers =
"Authorization": `Bearer $store.getState().authState.user?.token`
;
return request;
);
export default tokenAxios;
另外,请确保您的授权中间件工作正常。
【讨论】:
以上是关于React - 注销组件问题的主要内容,如果未能解决你的问题,请参考以下文章
顺便说一句,我是一名学生,如何在汉堡菜单中的 React on Rails 中添加注销