#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 в своем приложении:
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, чтобы увидеть, что он отправляет необходимую мне информацию в редуктор, чтобы продолжить вход в систему, есть другие идеи?