【问题标题】:Firebase Auth with ReactNative Redux not working using Expo带有 ReactNative Redux 的 Firebase Auth 无法使用 Expo
【发布时间】:2021-05-02 13:54:21
【问题描述】:

我正在尝试在我的项目中实现 Firebase 身份验证,我能够通过 Firebase 的 REST API 来实现(至少对于 Firebase 中的电子邮件和密码身份验证)。但是,我需要使用社交身份验证(Facebook 和 Google,至少除了电子邮件和密码)。

我正在为我的项目使用 EXPO(这就是我没有 iOS 或 Android 文件夹的原因)。

我已经在 package.json 中安装了 Firebase 依赖项:

我还在我的应用上初始化了 Firebase:

FirebaseKeys.js 文件:

export default {
    firebaseConfig: {
        apiKey: "[myApi Key]",
        authDomain: "alianzafc2021.firebaseapp.com",
        databaseURL: "https://alianzafc2021-default-rtdb.firebaseio.com",
        projectId: "alianzafc2021",
        storageBucket: "alianzafc2021.appspot.com",
        messagingSenderId: "[Messaging ID]",
        appId: "[App ID]",
        measurementId: "[Measurement ID}]"
      }
}

App.js:(我在哪里初始化 Firebase)

import React, { useState } from 'react';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import ReduxThunk from 'redux-thunk';
import * as Font from 'expo-font';
import * as firebase from 'firebase';

//Firebase Initialize Config
import firebaseConfig from "./constants/FireBaseKeys";

//Navigation
import AppNavigator from "./navigation/AppNavigator";

//import de reducers
import jugadoresReducer from "./store/reducers/jugadores";
import noticiasReducer from "./store/reducers/noticias";
import partidosReducer from "./store/reducers/partido";
import tablaReducer from "./store/reducers/tabla";
import authReducer from "./store/reducers/auth";

if (!firebase.apps.length) {
  firebase.initializeApp(firebaseConfig);
}else {
  firebase.app(); // if already initialized, use that one
}

const rootReducer = combineReducers({
  jugadores: jugadoresReducer,
  noticias: noticiasReducer,
  partidos: partidosReducer,
  tabla: tablaReducer,
  auth: authReducer,
});

const store = createStore(rootReducer,applyMiddleware(ReduxThunk));

//Recibe la Tipografia de numeros
const fetchFonts = () => {
  return Font.loadAsync({
    'number': require('./assets/fonts/number.ttf'),
  });
};

export default function App() {
  const [fontLoaded, setFontLoaded] = useState(true);

  if (!fontLoaded) {
    return (
      <AppLoading 
      startAsync={fetchFonts} 
      onFinish={() => {
        setFontLoaded(true);
      }} />
    );
  }

  return (
    <Provider store={store}>
      <AppNavigator />
    </Provider>
  );
}

我还创建了没有功能的登录页面(只是尝试登录,这给出了错误):

    import React, {useState,useCallback} from 'react';
import {
  View,
  Text,
  TouchableOpacity,
  Image,
  Platform,
  StyleSheet,
  ScrollView
} from 'react-native';
import { useDispatch } from 'react-redux';

import FormInput from '../components/UI/FormInput';
import FormButton from '../components/UI/FormButton';
import SocialButton from '../components/UI/SocialButton';
import * as authActions from '../store/actions/auth';

const LoginScreen = ({navigation}) => {
    const [email, setEmail] = useState();
    const [password, setPassword] = useState();
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState();
    const dispatch = useDispatch();

    const LoginHandler = useCallback ( async (email, password) => {
      setError(null);
        setIsLoading(true);
        try {
            await dispatch(authActions.login(email,password));
            //props.navigation.navigate('Shop');
        } catch (err) {
            setError(err.message);
            setIsLoading(false);
        }
    }, [dispatch, setEmail, setPassword]);

  return (
    <ScrollView contentContainerStyle={styles.container}>
      <Image
        source={require('../assets/alianza-logo.png')}
        style={styles.logo}
      />
      <Text style={styles.text}>Albo App</Text>

      <FormInput
        labelValue={email}
        onChangeText={(userEmail) => setEmail(userEmail)}
        placeholderText="Correo"
        iconType="user"
        keyboardType="email-address"
        autoCapitalize="none"
        autoCorrect={false}
      />

      <FormInput
        labelValue={password}
        onChangeText={(userPassword) => setPassword(userPassword)}
        placeholderText="Contraseña"
        iconType="lock"
        secureTextEntry={true}
      />

      <FormButton
        buttonTitle="Ingresar"
        onPress={LoginHandler(email, password)}
      />

      <TouchableOpacity style={styles.forgotButton} onPress={() => {}}>
        <Text style={styles.navButtonText}>Olvidaste la Contraseña?</Text>
      </TouchableOpacity>

      {Platform.OS === 'android' ? (
        <View>
          <SocialButton
            buttonTitle="Ingresa con Facebook"
            btnType="facebook"
            color="#4867aa"
            backgroundColor="#e6eaf4"
            onPress={() => {}}
          />

          <SocialButton
            buttonTitle="Ingresa con Google"
            btnType="google"
            color="#de4d41"
            backgroundColor="#f5e7ea"
            onPress={() => {}}
          />
        </View>
      ) : null}

      <TouchableOpacity
        style={styles.forgotButton}
        onPress={() => navigation.navigate('Signup')}>
        <Text style={styles.navButtonText}>
          Crear una Cuenta
        </Text>
      </TouchableOpacity>
    </ScrollView>
  );
};

export default LoginScreen;

const styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
    paddingTop: 50,
    marginTop: 30

  },
  logo: {
    height: 150,
    width: 150,
    resizeMode: 'cover',
  },
  text: {
    fontSize: 28,
    marginBottom: 10,
    color: '#051d5f',
  },
  navButton: {
    marginTop: 15,
  },
  forgotButton: {
    marginVertical: 35,
  },
  navButtonText: {
    fontSize: 18,
    fontWeight: '500',
    color: '#2e64e5'
  },
});

现在我的问题是我无法提供功能以在我的 Reducer 上登录时保存一些信息(甚至无法在我的操作上到达登录事件)。

这是我的 Actions 文件(我注释掉了使用 Rest API 登录时有效的代码,我只修改了登录功能)

import AsyncStorage from '@react-native-community/async-storage';
import Auth from "firebase/firebase-auth";

//export const SIGNUP = 'SIGNUP';
//export const LOGIN = 'LOGIN';
export const AUTHENTICATE = 'AUTHENTICATE';
export const LOGOUT = 'LOGOUT';
export const SET_DID_TRY_AL = 'SET_DID_TRY_AL';

let timer;

export const setDidTryAL = () => {
    return { type: SET_DID_TRY_AL };
};

export const authenticate = (userId, token, expiryTime) => {
    return dispatch => {
        dispatch(setLogoutTimer(expiryTime));
        dispatch({ type: AUTHENTICATE, userId: userId, token: token });
    }
}

export const signup = (email, password) => {
    return async dispatch => {
        const response = await fetch('https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[Here my ID Key]', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                email: email,
                password: password,
                returnSecureToken: true,
            })
        });
        if (!response.ok) {
            const errorResData = await response.json();
            const errorId = errorResData.error.message;
            let message = 'Algo salio mal!';
            if (errorId === 'EMAIL_EXISTS') {
                message = 'Este Correo ya esta en Uso';
            }
            throw new Error(message);
        }

        const resData = await response.json();
        console.log(resData.localId);
        dispatch(
            authenticate(
                resData.localId,
                resData.idToken,
                parseInt(resData.expiresIn) * 1000
            )
        );
        const expirationDate = new Date(new Date().getTime() + parseInt(resData.expiresIn) * 1000);
        saveDataToStorage(resData.idToken, resData.localId, expirationDate);
    };
};

export const login = (email, password) => {

    return async dispatch => {
     await firebase
            .auth()
            .signInWithEmailAndPassword(email, password)
            .then(resData => {
                console.log(resData);
                dispatch(
                    authenticate(
                        resData.localId,
                        resData.idToken,
                        parseInt(resData.expiresIn) * 1000,
                    )
                )
                const expirationDate = new Date(new Date().getTime() + parseInt(resData.expiresIn) * 1000);
                saveDataToStorage(resData.idToken, resData.localId, expirationDate);
            })
            .catch(err => { console.log(err) });
    }

    // return async dispatch => {
    //     const response = await fetch('https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[Here my ID Key]', {
    //         method: 'POST',
    //         headers: {
    //             'Content-Type': 'application/json',
    //         },
    //         body: JSON.stringify({
    //             email: email,
    //             password: password,
    //             returnSecureToken: true,
    //         })
    //     });
    //     if (!response.ok) {
    //         const errorResData = await response.json();
    //         const errorId = errorResData.error.message;
    //         let message = 'Algo salio mal!';
    //         if (errorId === 'EMAIL_NOT_FOUND') {
    //             message = 'Este Correo no esta en Uso';
    //         } else if (errorId === 'INVALID_PASSWORD') {
    //             message = 'Esta Contraseña esta Equivocada.'
    //         }

    //         throw new Error(message);
    //     }

    //     const resData = await response.json();
    //     console.log(resData);
    //     dispatch(
    //         authenticate(
    //             resData.localId,
    //             resData.idToken,
    //             parseInt(resData.expiresIn) * 1000,
    //         )
    //     );
    //     const expirationDate = new Date(new Date().getTime() + parseInt(resData.expiresIn) * 1000);
    //     saveDataToStorage(resData.idToken, resData.localId, expirationDate);
    // };
};

export const logout = () => {
    clearLogoutTimer();
    AsyncStorage.removeItem('userData');
    return { type: LOGOUT };
};

const clearLogoutTimer = () => {
    if (timer) {
        clearTimeout(timer);
    }
};

const setLogoutTimer = expirationTime => {
    return dispatch => {
        timer = setTimeout(() => {
            dispatch(logout());
        }, expirationTime);
    };
}

const saveDataToStorage = (token, userId, expirationDate) => {
    AsyncStorage.setItem('userData', JSON.stringify({
        token: token,
        userId: userId,
        expiryDate: expirationDate.toISOString(),
    }))
};

export const recoverPassword = (email) => {
    return async dispatch => {
        const response = await fetch('https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=[Here my ID Key]', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                requestType: "PASSWORD_RESET",
                email: email
            })
        });
        if (!response.ok) {
            const errorResData = await response.json();
            const errorId = errorResData.error.message;
            let message = 'Algo salio mal!';
            if (errorId === 'EMAIL_NOT_FOUND') {
                message = 'Este Correo no esta en Uso';
            }
            throw new Error(message);
        }

        const resData = await response.json();
    };
};

还有我的减速机:

import { AUTHENTICATE, LOGOUT, SET_DID_TRY_AL } from "../actions/auth";

const initialState = {
    token: null,
    userId: null,
    didTryAutoLogin: false,
};

export default(state = initialState, action) => {
    switch (action.type) {
        case AUTHENTICATE:
            return{
                token: action.token,
                userId: action.userId,
                didTryAutoLogin: true,
            };
        case SET_DID_TRY_AL:
            return {
                ...state,
                didTryAutoLogin: true,
            }
        case LOGOUT:
            return {
                ...initialState,
                didTryAutoLogin: true,
            };
       // case SIGNUP:
        //    return{
         //       token: action.token,
         //       userId: action.userId,
         //   };
        default:
            return state;
    }
};

我得到的错误是:

错误:重新渲染过多。 React 限制渲染次数以防止无限循环。

我尝试解决添加回调函数的问题,但仍然出现同样的问题,而且我什至无法在 Actions 上调度我的函数。

任何想法我可以为此做些什么?

亲切的问候

【问题讨论】:

    标签: firebase react-native redux callback expo


    【解决方案1】:

    太多re-render的错误在这里:

    你不应该像在渲染方法上那样调用 onPress。

    它在 onPress 中缺少像 onPress={() =&gt; yourFunction()} 这样的回调函数,而您正在像 onPress={yourFunction()} 那样调用

    原文:

          <FormButton
            buttonTitle="Ingresar"
            onPress={LoginHandler(email, password)}
          />
    

    正确

          <FormButton
            buttonTitle="Ingresar"
            onPress={() => LoginHandler(email, password)}
          />
    

    【讨论】:

    • 嘿,谢谢你,它确实解决了多个调用的问题,关于登录功能的任何想法,现在它可以登录,但似乎对代码没有任何作用:
    • return async dispatch =&gt; { await firebase .auth() .signInWithEmailAndPassword(email, password) .then(resData =&gt; { console.log(resData); dispatch( authenticate( resData.localId, resData.idToken, parseInt(resData.expiresIn) * 1000, ) )
    • ` const expireDate = new Date(new Date().getTime() + parseInt(resData.expiresIn) * 1000); saveDataToStorage(resData.idToken, resData.localId, expireDate); }) .catch(err => { console.log(err) }); }`
    • 在登录操作中你不能返回异步。你应该这样返回return (dispatch) =&gt; {...}
    • 谢谢 Erick,我似乎仍然无法访问 ResData 控制台日志以查看它是否将我需要的信息发送到 Reducer,以继续登录,还有其他想法吗?
    猜你喜欢
    • 2021-05-02
    • 2019-10-22
    • 2018-08-23
    • 2019-01-19
    • 1970-01-01
    • 1970-01-01
    • 2016-11-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多