在这种情况下检测 Firebase 登录何时完成或取消

Detect when Firebase sign in has finished or canceled in this scenario

我学习了 ReactJs,现在我必须登录 Firebase。我有一个关于如何检测 Firebase linkWithPopup 何时完成的设计问题。用户按下 Button,我的匿名用户 Firebase UID 将变成 Google 凭据。

linkWithPopup 弹出,用户 select 一个 Google 帐户要使用。

我必须检测此进程何时完成或中止。

这是我的代码:

当用户单击 Google 登录按钮时将调用此方法:

onSocialLoginLink = provider => {
    const { firebase, changeUserRole } = this.props;
    firebase.auth.currentUser
        .linkWithPopup(firebase[provider])
        // .linkWithRedirect(this.props.firebase[provider])
        .then(changeUserRole())
        .then(this.fetchSignInMethods)
        .catch(error => this.setState({ error }));
};

我遇到的问题是 changeUserRole()linkWithPopup returns 之前被调用,因为 linkWithPopup 当然 运行 是异步的。这意味着即使用户 select 中止登录,我的用户也会从 changeUserRole() 获得这个新角色。

我必须检测此进程何时完成或中止。

推荐的最佳方法是什么? 我的 ide 是,如果我可以检测到 signin window 何时失去焦点并重新获得焦点,我可以查看 Firebase 用户是否已将 provider 更改为 Google 或者仍然是匿名用户?这可行吗?

这是处理登录的Component

/* eslint-disable max-classes-per-file */
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { compose } from 'recompose';
import { AuthUserContext, withAuthorization } from '../../session';
import { withFirebase } from '../../firebase';
import { SIGN_IN_METHODS } from '../../constants/signinmethods';
import * as ROLES from '../../constants/roles';
import '../../styles/link-account.scss';
import { changeToUserRole } from '../../redux/userData/user.actions';

class LoginManagementBase extends Component {
    constructor() {
        super();
        this.state = {
            activeSignInMethods: [],
            anonymousSignIn: null,
            error: null,
        };
    }

    componentDidMount() {
        this.fetchSignInMethods();
    }

    fetchSignInMethods = () => {
        const { firebase, authUser } = this.props;
        const email = authUser.email === null ? 'none@guest.ac' : authUser.email;
        firebase.auth
            .fetchSignInMethodsForEmail(email)
            .then(activeSignInMethods =>
                this.setState({
                    activeSignInMethods,
                    anonymousSignIn: activeSignInMethods.length === 0,
                    error: null,
                }),
            )
            .catch(error => this.setState({ error }));
    };

    onSocialLoginLink = provider => {
        const { firebase, changeUserRole } = this.props;
        firebase.auth.currentUser
            .linkWithPopup(firebase[provider])
            // .linkWithRedirect(this.props.firebase[provider])
            .then(changeUserRole())
            .then(this.fetchSignInMethods)
            .catch(error => this.setState({ error }));
    };

    onDefaultLoginLink = password => {
        const { firebase, authUser } = this.props;
        const credential = firebase.emailAuthProvider.credential(authUser.email, password);

        firebase.auth.currentUser
            .linkAndRetrieveDataWithCredential(credential)
            .then(this.fetchSignInMethods)
            .catch(error => this.setState({ error }));
    };

    onUnlink = providerId => {
        const { firebase } = this.props;
        firebase.auth.currentUser
            .unlink(providerId)
            .then(this.fetchSignInMethods)
            .catch(error => this.setState({ error }));
    };

    render() {
        const { activeSignInMethods, error } = this.state;
        const { saveRolesErr, isSavingRole } = this.props;
        // if (isSavingRole) return null;
        return (
            <div className="provideToggler">
                &nbsp;&nbsp;&nbsp;
                <h1>
                    You are signed in Anonymously!
                    <br />
                    Changes you do is only saved in this browser.
                    <br /> If you want to access your progress anywhere please sign in below!
                </h1>
                &nbsp;
                <ul>
                    {SIGN_IN_METHODS.map(signInMethod => {
                        const onlyOneLeft = activeSignInMethods.length === 1;
                        const isEnabled = activeSignInMethods.includes(signInMethod.id);
                        return (
                            <li key={signInMethod.id}>
                                {signInMethod.id === 'password' ? (
                                    <DefaultLoginToggle
                                        // accountEmail={this.props.authUser.email}
                                        onlyOneLeft={onlyOneLeft}
                                        isEnabled={isEnabled}
                                        signInMethod={signInMethod}
                                        onLink={this.onDefaultLoginLink}
                                        onUnlink={this.onUnlink}
                                    />
                                ) : (
                                    <SocialLoginToggle
                                        onlyOneLeft={onlyOneLeft}
                                        isEnabled={isEnabled}
                                        signInMethod={signInMethod}
                                        onLink={this.onSocialLoginLink}
                                        onUnlink={this.onUnlink}
                                    />
                                )}
                            </li>
                        );
                    })}
                </ul>
                <h1 style={{ color: 'red' }}>
                    {error && error.message}
                    {saveRolesErr && saveRolesErr.message}
                </h1>
            </div>
        );
    }
}

const SocialLoginToggle = ({ onlyOneLeft, isEnabled, signInMethod, onLink, onUnlink }) =>
    isEnabled ? (
        <button type="button" onClick={() => onUnlink(signInMethod.id)} disabled={onlyOneLeft}>
            Unlink <i className={signInMethod.icon} aria-hidden="true" /> {signInMethod.name} sign in
        </button>
    ) : (
        <button type="button" onClick={() => onLink(signInMethod.provider)}>
            Link <i className={signInMethod.icon} aria-hidden="true" /> {signInMethod.name} sign in
        </button>
    );

// TODO This is not in use but might use it later
class DefaultLoginToggle extends Component {
    constructor() {
        super();
        this.state = { passwordOne: '', passwordTwo: '' };
    }

    onSubmit = event => {
        const { passwordOne } = this.state;
        const { onLink } = this.props;
        event.preventDefault();
        onLink(passwordOne);
        this.setState({ passwordOne: '', passwordTwo: '' });
    };

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

    render() {
        const { signInMethod } = this.props;
        const { passwordOne, passwordTwo } = this.state;
        const isInvalid = passwordOne !== passwordTwo || passwordOne === '';
        return (
            <form onSubmit={this.onSubmit}>
                Link <i className={signInMethod.icon} aria-hidden="true" /> {signInMethod.name} sign in
                <input
                    name="passwordOne"
                    value={passwordOne}
                    onChange={this.onChange}
                    type="password"
                    placeholder="Password for email sign in"
                />
                <input
                    name="passwordTwo"
                    value={passwordTwo}
                    onChange={this.onChange}
                    type="password"
                    placeholder="Confirm New Password"
                />
                <button disabled={isInvalid} type="submit">
                    Save password for email sign in
                </button>
            </form>
        );
    }
}

const mapDispatchToProps = dispatch => ({
    changeUserRole: () => dispatch(changeToUserRole()),
});

const mapStateToProps = state => {
    return {
        isSavingRole: state.user.isSavingRoles,
        saveRolesErr: state.user.saveRolesErrMsg,
    };
};

const enhance = compose(withFirebase, connect(mapStateToProps, mapDispatchToProps));
const LoginManagement = enhance(LoginManagementBase);
const LinkAccounts = () => (
    <AuthUserContext.Consumer>
        {authUser => (
            <div>
                <LoginManagement authUser={authUser} />
            </div>
        )}
    </AuthUserContext.Consumer>
);

const condition = authUser => authUser && authUser.roles.includes(ROLES.ANON);
export default withAuthorization(condition)(LinkAccounts);

如果这是问题所在,您可以添加一个条件来检查而不是更改角色

    const { firebase, changeUserRole } = this.props;
    firebase.auth.currentUser
        .linkWithPopup(firebase[provider])
        .then(res=>{
              if(res.credential){changeUserRole()}
         })
        .then(this.fetchSignInMethods)
        .catch(error => this.setState({ error }));
};```