首页 > 解决方案 > 如何使用firebase + react检查Session是否还活着?

问题描述

我已经添加了 Firebase 的授权,它工作得很好。可以登录应用程序并导航,但是当我使用firebase.auth().signOutobservable时onAuthChanged不会更改/未触发。

对于正确的登录名/密码(错误组合为 400) - 会话已保存,并且我拥有用户凭据:

import React, {useContext, useEffect} from 'react';
import {ROUTES} from '../../../constants';
import {AuthUserContext} from "../../../session";
import history from '../../../helpers/history';
import {useLocation} from "react-router";
import app from "../../../api/firebase";

const WithAuthorization: React.FC = ({children}) => {
    const authUser = useContext(AuthUserContext);
    const isLogin = useLocation().pathname === ROUTES.LOGIN;
    const pushLogin = () => !isLogin && history.push(ROUTES.LOGIN);

    useEffect(() => {
        const listener = app.auth().onAuthStateChanged(
            (user: any) => {
                if(!user) {
                    pushLogin()
                } else {
                    console.log('Signed in with user');
                    console.log(user);
                }
            },
            (e: any) => {
                console.log(e);
            }, () => {
                console.log('completed');
            });
        return listener();
    }, [])

    return <>
        {authUser ? children : pushLogin()}
    </>;
}

export default WithAuthorization;

但是,当应用程序刷新时,我想检查会话是否处于活动状态。在查看我发现onAuthChanged observable 的文档时,这看起来很简单,但实际上只有在我登录时才会触发。

刷新页面后,或者当我触发时signOut- 它什么也不做。

这是授权保护组件,它包裹了整个 App:

import React, {useContext, useEffect} from 'react';
import {ROUTES} from '../../../constants';
import {AuthUserContext} from "../../../session";
import history from '../../../helpers/history';
import app from "../../../api/firebase";

const WithAuthorization: React.FC = ({children}) => {
    const authUser = useContext(AuthUserContext);
    const pushLogin = () => history.push(ROUTES.LOGIN);

    useEffect(() => {
        const listener = app.auth().onAuthStateChanged(
            (user: any) => {
                if(!user) pushLogin()
            },
            (e: any) => {
                console.log(e);
            }, () => {
                console.log('completed');
            });
        return listener();
    }, [])

    return <>
        {authUser ? children : pushLogin()}
    </>;
}

export default WithAuthorization;

我是否缺少身份验证保护组件或可观察的东西?

--- 应用结构:

App 组件非常简单:


    import React, {useState} from 'react';
    import { Route, Switch, useLocation } from 'react-router';
    import { Header, WithAuthorization } from './common';
    import DeviceSelection from './DeviceSelection';
    import PerfectScroll from 'react-perfect-scrollbar';
    import NotFound from './NotFound';
    import ThankYou from "./Thankyou";
    
    import 'react-perfect-scrollbar/dist/css/styles.css';
    import './App.scss';
    import {ROUTES} from "../constants";
    import Login from "./Login";
    import {AuthUserContext} from "../session";
    
    const App = () => {
        const {pathname} = useLocation();
        const [authUser, setAuthUser] = useState(null as any);
        const isThankYou = pathname === ROUTES.THANKYOU;
    
        return (
            <AuthUserContext.Provider
                value={authUser}
            >
                <WithAuthorization>
                    {!isThankYou && <Header authUser={authUser}/>}
                </WithAuthorization>
    
                    <div className={`${!isThankYou ? 'appScrollContainer' : ''}`}>
                        <PerfectScroll>
                            <Switch>
                                <Route exact path={[ROUTES.ROOT, ROUTES.HOME]} component={() => <WithAuthorization><DeviceSelection/></WithAuthorization>} />
                                <Route path={ROUTES.THANKYOU} component={() => <WithAuthorization><ThankYou/></WithAuthorization>} />
                                <Route path={ROUTES.LOGIN} component={() => <Login setAuthUser={(user: any) => setAuthUser(user)} />}/>
                                <Route path="*" component={NotFound} />
                            </Switch>
                        </PerfectScroll>
                    </div>
            </AuthUserContext.Provider>
        );
    }
    
    export default App;

Signout 来自 Header 内的一个按钮,该按钮也包含在 WithAuthorization 中:

    <Button label={'Sign out'} click={() => app.auth().signOut()} />

登录只做一件事,如果登录成功则重定向到 /home:

    import React, {useState} from 'react';
    import TextInput from "../common/TextInput";
    import history from '../../helpers/history';
    import {ROUTES} from "../../constants";
    import app, {signInWithEmailAndPassword} from "../../api/firebase";
    
    interface Props {
        setAuthUser: (user: any) => void,
    }
    
    const Login: React.FC<Props> = ({setAuthUser}) => {
        const [form, updateForm] = useState({login: '', password: ''});
    
        const authorize = (user: string, password: string) => {
            app.auth().setPersistence(app.auth.Auth.Persistence.SESSION)
                .then(() => {
                    return signInWithEmailAndPassword(user, password).then((user: any) => {
                        if(user) {
                            setAuthUser(user);
                            history.push(ROUTES.ROOT);
                            return user
                        }
                        return null
                    })
                })
                .catch((e: any) => {
                    console.log(e);
                })
        }
    
        return <div className='form'>
            <TextInput
                type="text"
                placeholder='login'
                name={'login'}
                value={form.login}
                label='Login'
                onChange={(e) => updateForm({...form, login: e.currentTarget.value})}
            />
            <TextInput
                type="password"
                placeholder='password'
                name={'password'}
                value={form.password}
                label='Password'
                onChange={(e) => updateForm({...form, password: e.currentTarget.value})}
            />
            <button onClick={() => authorize(form.login, form.password)}>Submit</button>
        </div>
    }
    
    export default Login;

FIrebase 使用本身:

    import firebase from 'firebase/app';
    import 'firebase/firestore';
    import 'firebase/auth';
    
    import {DEV_LOCAL_CONFIG, DEV_REMOTE_CONFIG, ORDERS_COLLECTION} from "./const";
    
    firebase.initializeApp(window.location.hostname !== 'localhost' ? DEV_LOCAL_CONFIG : DEV_REMOTE_CONFIG);
    
    /* ==== Authorization ==== */
    const signInWithEmailAndPassword = (email: string, password: string) =>
        firebase.auth().signInWithEmailAndPassword(email, password);
    
    const signOut = () => firebase.auth().signOut();
    
    export default firebase;
    
    export {
        signInWithEmailAndPassword,
        signOut
    }

标签: javascriptreactjsfirebasefirebase-authentication

解决方案


我的错误仅在于这一行:

return listener();

当我在中定义监听器时useEffect,它会立即取消订阅。应该:

return () => listener()

除此之外,一切正常。


推荐阅读