Аутентификация Firebase с реактивным уменьшением не работает с использованием Expo

#firebase #react-native #redux #callback #expo

#firebase #react-native #redux #обратный вызов #expo

Вопрос:

Я пытаюсь реализовать аутентификацию Firebase в своем проекте, я смог это сделать (по крайней мере, для аутентификации по электронной почте и паролю в Firebase) через REST API из Firebase. Тем не менее, мне нужно работать с социальной аутентификацией (Facebook и Google, помимо электронной почты и пароля, по крайней мере).

Я использую EXPO для своего проекта (вот почему у меня нет папок iOS или Android).

Я уже установил зависимость Firebase в свой 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'
  },
});
 

Страница входа в систему

Теперь моя проблема в том, что я не могу предоставить функциональность для сохранения некоторой информации при входе в мой редуктор (или даже для достижения события входа в систему в моих действиях).

Вот мой файл действий (я закомментировал код, который работает при использовании 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.

Есть идеи, что я могу для этого сделать?

С уважением

Ответ №1:

Ошибка слишком большого повторного рендеринга здесь:

Вы никогда не должны вызывать onPress подобным образом при методе рендеринга.

Отсутствует функция обратного вызова в onPress like onPress={() => yourFunction()} , и вы вызываете like onPress={yourFunction()}

Оригинал:

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

Правильно

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

Комментарии:

1. Привет, спасибо вам за то, что это решает проблему с несколькими вызовами, есть идеи по функции входа в систему, так как теперь она переходит к логину, но, похоже, это ничего не делает с кодом :

2. return async dispatch => { await firebase .auth() .signInWithEmailAndPassword(email, password) .then(resData => { console.log(resData); dispatch( authenticate( resData.localId, resData.idToken, parseInt(resData.expiresIn) * 1000, ) )

3. ` const ExpirationDate = новая дата(новая дата().getTime() parseInt(resData.expiresIn) * 1000); saveDataToStorage(resData.idToken, resData.localID, ExpirationDate); }) .catch(ошибка => {console.log(ошибка) }); }`

4. При входе в систему вы не можете вернуть async. Вы должны вернуться так return (dispatch) => {...}

5. Спасибо, Эрик, я все еще не могу получить доступ к журналу консоли ResData, чтобы увидеть, что он отправляет необходимую мне информацию в редуктор, чтобы продолжить вход в систему, есть другие идеи?