简体   繁体   中英

Promise chaining for Async once() call to only continue once actual result is returned

Asking this since my last question got closed while none of the examples helped at all. I have some invite codes set up in a firebase realtime database, and I am checking the Invite Code input by a user when they are using the SignUp page. Once they click subit, the 'doSubmit' function triggers, and the first part of that function uses a function in the firebase.js file to check if the invite code exists. If it does, the function is supposed to return 'ADMIN' and if not it returns 'GENERAL'. I now see that once() is an Async function, so I need to implement some sort of promise chaining for the checkInviteCodes function. I tried adding promise chaining when calling the checkInviteCode, but was giving me some errors saying that checkInviteCodes is not a function. I also tried adding some chaining inside the checkInviteCodes function but still did not get the desired result. The indes.js and Firebase.js files are below and any help is appreciated.

../SignUp/index.js

import React, {useState} from 'react';
import {Link as RouterLink, withRouter} from 'react-router-dom'
import {compose} from 'recompose';
import {withFirebase} from '../Firebase'

import * as ROUTES from '../../constants/routes'

import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import TextField from '@material-ui/core/TextField';
import Link from '@material-ui/core/Link';
import Grid from '@material-ui/core/Grid';
import Box from '@material-ui/core/Box';
import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
import Typography from '@material-ui/core/Typography';
import {makeStyles} from '@material-ui/core/styles';
import Container from '@material-ui/core/Container';
import {SignInLink} from "../SignIn";

function Copyright() {
    return (
        <Typography variant="body2" color="textSecondary" align="center">
            {'Copyright © '}
            <Link color="inherit" href="https://material-ui.com/">
                Your Website
            </Link>{' '}
            {new Date().getFullYear()}
            {'.'}
        </Typography>
    );
}

const useStyles = makeStyles((theme) => ({
    paper: {
        marginTop: theme.spacing(8),
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
    },
    avatar: {
        margin: theme.spacing(1),
        backgroundColor: theme.palette.secondary.main,
    },
    form: {
        width: '100%', // Fix IE 11 issue.
        marginTop: theme.spacing(3),
    },
    submit: {
        margin: theme.spacing(3, 0, 2),
    },
}));


const SignUpPage = () => (
    <div>
        <SignUpForm/>
    </div>
);

const SignUpFormBase = (props) => {
    const classes = useStyles();
    const [ username, setUsername ] = useState('');
    const [ email, setEmail ] = useState('');
    const [ passwordOne, setPasswordOne ] = useState('');
    const [ passwordTwo, setPasswordTwo ] = useState('');
    const [ inviteCode, setInviteCode] = useState('');
    const [ error, setError ] = useState(null);

    const isInvalid =
        passwordOne !== passwordTwo ||
        passwordOne === '' ||
        email === '' ||
        username === '';

    const doSubmit = event => {

        const role = props.firebase.checkInviteCode(inviteCode);

        console.log('Logging invite code and role');
        console.log(inviteCode);
        console.log(role);

        props.firebase
            .doCreateUserWithEmailAndPassword(email, passwordOne)
            .then(authUser => {
                return props.firebase
                    .user(authUser.user.uid)
                    .set({
                        username,
                        email,
                        role,
                    });
            })
            .then(authUser => {
                setUsername(username);
                setPasswordOne(passwordOne);
                setPasswordTwo(passwordTwo);
                setEmail(email);
                setInviteCode(inviteCode);
                props.history.push(ROUTES.HOME);
            })
            .catch(error => {
                setError(error);
                console.log(error);
            });

        event.preventDefault();
    }

    return (
        <Container component="main" maxWidth="xs">
            <CssBaseline/>
            <div className={classes.paper}>
                <Avatar className={classes.avatar}>
                    <LockOutlinedIcon/>
                </Avatar>
                <Typography component="h1" variant="h5">
                    Sign up
                </Typography>
                <form className={classes.form} noValidate onSubmit={doSubmit}>
                    <Grid container spacing={2}>
                        <Grid item xs={12}>
                            <TextField
                                autoComplete="fname"
                                name="username"
                                variant="outlined"
                                required
                                fullWidth
                                id="username"
                                label="Username"
                                autoFocus
                                value={username}
                                onChange={event => setUsername(event.target.value)}
                            />
                        </Grid>
                        <Grid item xs={12}>
                            <TextField
                                variant="outlined"
                                required
                                fullWidth
                                id="email"
                                label="Email Address"
                                name="email"
                                autoComplete="email"
                                value={email}
                                onChange={event => setEmail(event.target.value)}
                            />
                        </Grid>
                        <Grid item xs={12}>
                            <TextField
                                variant="outlined"
                                required
                                fullWidth
                                name="password"
                                label="Password"
                                type="password"
                                id="password"
                                autoComplete="current-password"
                                value={passwordOne}
                                onChange={event => setPasswordOne(event.target.value)}
                            />
                        </Grid>
                        <Grid item xs={12}>
                            <TextField
                                variant="outlined"
                                required
                                fullWidth
                                name="password"
                                label="Confirm Password"
                                type="password"
                                id="password"
                                autoComplete="confirm-password"
                                value={passwordTwo}
                                onChange={event => setPasswordTwo(event.target.value)}
                            />
                        </Grid>
                        <Grid item xs={12}>
                            <TextField
                                variant="outlined"
                                required
                                fullWidth
                                name="inviteCode"
                                label="InviteCode"
                                id="inviteCode"
                                autoComplete="inviteCode"
                                value={inviteCode}
                                onChange={event => setInviteCode(event.target.value)}
                            />
                        </Grid>
                    </Grid>
                    <Button
                        type="submit"
                        fullWidth
                        variant="contained"
                        className={classes.submit}
                        disabled={isInvalid}
                    >
                        Sign Up
                    </Button>
                    {error && <p>{error.message}</p>}
                    <Grid container justifyContent="flex-end">
                        <Grid item>
                            <SignInLink href="#" variant="body2"/>
                        </Grid>
                    </Grid>
                </form>
            </div>
            <Box mt={5}>
                <Copyright/>
            </Box>
        </Container>
    )
}

const SignUpLink = () => (
    <p>
        <RouterLink to={ROUTES.SIGN_UP}>Don't have an account? Sign Up</RouterLink>
    </p>
);

const SignUpForm = compose(
    withFirebase,
    withRouter,
)(SignUpFormBase);

export default SignUpPage;

export {SignUpForm, SignUpLink};
firebase.js

import app from 'firebase/app';
import 'firebase/auth';
import 'firebase/database';
import * as ROLES from '../../constants/roles';

const config = {
    apiKey: process.env.REACT_APP_API_KEY,
    authDomain: process.env.REACT_APP_AUTH_DOMAIN,
    databaseURL: process.env.REACT_APP_DATABASE_URL,
    projectId: process.env.REACT_APP_PROJECT_ID,
    storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
};

class Firebase {
    constructor() {
        app.initializeApp(config);

        this.auth = app.auth();
        this.db = app.database();
    }

    // *** Auth API ***

    doCreateUserWithEmailAndPassword = (email, password) =>
        this.auth.createUserWithEmailAndPassword(email, password);

    doSignInWithEmailAndPassword = (email, password) =>
        this.auth.signInWithEmailAndPassword(email, password);

    doSignOut = () => this.auth.signOut();

    doPasswordReset = email => this.auth.sendPasswordResetEmail(email);

    doPasswordUpdate = password =>
        this.auth.currentUser.updatePassword(password);

    // *** Merge Auth and DB User API ***

    onAuthUserListener = (next, fallback) =>
        this.auth.onAuthStateChanged(authUser => {
            if (authUser) {
                this.user(authUser.uid)
                    .once('value')
                    .then(snapshot => {
                        const dbUser = snapshot.val();

                        // default empty roles
                        if (!dbUser.role) {
                            dbUser.role = {};
                        }

                        // merge auth and db user
                        authUser = {
                            uid: authUser.uid,
                            email: authUser.email,
                            ...dbUser,
                        };

                        next(authUser);
                    });
            } else {
                fallback();
            }
        });

    // *** User API ***

    user = uid => this.db.ref(`users/${uid}`);

    users = () => this.db.ref('users');

    checkInviteCode = code => {
        const role = '';

        this.db.ref(`invites`).orderByChild("value").equalTo(code).once('value').then(snapshot => {
            console.log('Found the value');
            if (snapshot.exists()) {
                console.log('Setting role to ADMIN');
                this.role = ROLES.ADMIN;
            } else {
                console.log('Setting role to GENERAL')
                this.role = ROLES.GENERAL;
            }
        })

        if (this.role !== ROLES.ADMIN)
            this.role = ROLES.GENERAL;

        console.log('Role in firebase.js : ' + this.role);
        return this.role;
    }
}

export default Firebase;
console log statements

Role in firebase.js : GENERAL - firebase.js:90 
Logging invite code and role - index.js:80  
testCode - index.js:81
GENERAL - index.js:82 
Found the value - firebase.js:77 
Setting role to ADMIN - firebase.js:79 

Putting this answer here to see if there is another way to do it that follows best practices. If not it might help someone else as well. Based on the comment by @Jaromanda X, I did a lot of attempts at making checkInviteCode return a promise. Once that was figured out I had to make the implementation of that function to use the returned promise as the user role. I was originally taking the returned promise and setting the role state to be the returned promise, but that was never setting the state before actually creating the user, so I went with using the returned promise directly to set the role as the user in the db was created. Below is the code for both the function and the implementation.

firebase.js

    checkInviteCode = code => {
        console.log('Checking invite code in firebase : ' + code);
        return this.db.ref(`invites`).orderByChild("value").equalTo(code).once('value').then(function(snapshot) {
            if (snapshot.exists()) {
                return ROLES.ADMIN;
            } else {
                return ROLES.GENERAL;
            }
        }, function(error) {
            return ROLES.GENERAL;
        });
    }

../SignUp/index.js

        props.firebase
            .checkInviteCode(inviteCode)
            .then(role => {
                props.firebase
                    .doCreateUserWithEmailAndPassword(email, passwordOne)
                    .then(authUser => {
                        return props.firebase
                            .user(authUser.user.uid)
                            .set({
                                username,
                                email,
                                role,
                            });
                    })
                    .then(authUser => {
                        setUsername(username);
                        setPasswordOne(passwordOne);
                        setPasswordTwo(passwordTwo);
                        setEmail(email);
                        setInviteCode(inviteCode);
                        props.history.push(ROUTES.HOME);
                    })
                    .catch(error => {
                        setError(error);
                    });
            })
            .catch(error => {
                // Implement something to do with error here
            });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM