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 HooksFunctional 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 &lt;token&gt;"

试试这个

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 中添加注销

来自根组件的 React/Redux 调度操作?

React Redux 不会重置某些组件的存储

React Redux:为啥这个嵌套组件没有从 redux 状态接收道具?

在未安装的组件上调用 setState() [重复]

通过本地存储的注销功能