authProvider calls "AUTH_ERROR" with only 2XX responses - react-admin

i have big issues debugging react-admin...
I have a node.js web server acting as an API which returns only 2XX responses but manages to trigger "AUTH_ERROR" inside my authProvider and i get Warning: Missing translation for key: "Cannot read property 'hasOwnProperty' of undefined" inside the console.
This is my networking when i trigger this issue:
as you can see no 4XX / 5XX codes only 2XX, the token gets removed from the localStorage causing the provider to go back to Login, but it shouldn't since I only got 2XX responses.
This is my code for the Auth Provider (it's the exemple for the documentation iirc)
// in src/authProvider.js
import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_GET_PERMISSIONS, AUTH_CHECK } from 'react-admin';
import decodeJwt from 'jwt-decode';
export default (type, params) => {
if (type === AUTH_LOGIN) {
const { username, password } = params;
const request = new Request('http://localhost:1337/api/v1/auth/login', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
})
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ token }) => {
const decodedToken = decodeJwt(token);
localStorage.setItem('token', token);
localStorage.setItem('role', decodedToken.role);
});
}
if (type === AUTH_LOGOUT) {
localStorage.removeItem('token');
localStorage.removeItem('role');
return Promise.resolve();
}
if (type === AUTH_ERROR) {
console.log("AUTH ERROR");
}
if (type === AUTH_CHECK) {
return localStorage.getItem('token') ? Promise.resolve() : Promise.reject();
}
if (type === AUTH_GET_PERMISSIONS) {
const role = localStorage.getItem('role');
return role ? Promise.resolve(role) : Promise.reject();
}
return Promise.reject('Unknown method');
};
This is "EDIT" form that causes this issue, all the other entities / CRUD operations do work ...
import React from "react";
import { List, Datagrid, TextField, ReferenceField, NumberField, Create, SimpleForm, TextInput, NumberInput, ReferenceInput, SelectInput, Edit, EditButton, DeleteButton, DisabledInput } from "react-admin";
export const CharacterList = (props) => (
<List {...props}>
<Datagrid>
<NumberField source="id" />
<TextField source="name" />
<TextField source="race" />
<TextField source="class" />
<NumberField source="level" />
<ReferenceField
label="user"
source="user_id"
reference="users"
linkType="show"
>
<TextField source="username" />
</ReferenceField>
<EditButton />
<DeleteButton />
</Datagrid>
</List>
);
export const CharacterCreate = (props) => (
<Create {...props}>
<SimpleForm>
<TextInput source="name"/>
<TextInput source="race"/>
<TextInput source="class"/>
<NumberInput source="level"/>
<ReferenceInput label="Owner" source="user_id" reference="users">
<SelectInput optionText="username" />
</ReferenceInput>
</SimpleForm>
</Create>
);
export const CharacterEdit = (props) => (
<Edit {...props}>
<SimpleForm>
<DisabledInput source="id"/>
<TextInput source="name"/>
<TextInput source="race"/>
<TextInput source="class"/>
<NumberInput source="level"/>
</SimpleForm>
</Edit>
);
I can provide more code samples if needed. And if some people has ideas on how to have more debug / verbose, because honestly this is one of the worst frameworks to debug ...
Thanks in advance everyone <3

Modifying the AuthProvider fixed the disconnection issue, coding a real AUTH_ERROR did wonders !
Now I'm left with the "OwnProperty" Error on Update on my Character entity...
New authProvider for reference in case somebody wants it:
import {AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK} from 'react-admin';
export default (type, params) => {
// called when the user attempts to log in
if (type === AUTH_LOGIN) {
const {username, password} = params;
console.log(username, password);
const request = new Request (process.env.REACT_APP_API_URL + '/auth/login', {
method: 'POST',
body: JSON.stringify ({username, password}),
headers: new Headers ({'Content-Type': 'application/json'}),
});
return fetch (request)
.then (response => {
if (response.status < 200 || response.status >= 300) {
throw new Error (response.statusText);
}
return response.json ();
})
.then (({token}) => {
localStorage.setItem ('token', token);
return Promise.resolve ();
});
}
if (type === AUTH_LOGOUT) {
localStorage.removeItem ('username');
return Promise.resolve ();
}
// called when the API returns an error
if (type === AUTH_ERROR) {
const {status} = params;
if (status === 401 || status === 403) {
localStorage.removeItem ('username');
return Promise.reject ();
}
return Promise.resolve ();
}
// called when the user navigates to a new location
if (type === AUTH_CHECK) {
return localStorage.getItem ('token')
? Promise.resolve ()
: Promise.reject ();
}
return Promise.reject ('Unknown method');
};

Related

Authentication flows | React Navigation 6 - How to get webToken?

When I learn authentication flow of react navigation 6.0, I read the sample code which used redux, it used dummy token, but In my react native project, I have to get the real token from server, So I tried to add some code in the sample project.
import * as React from 'react';
import { Button, Text, TextInput, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { useMutation, gql } from '#apollo/client';
import {
ApolloClient,
ApolloProvider,
createHttpLink,
InMemoryCache
} from '#apollo/client';
import { setContext } from 'apollo-link-context';
const SIGNIN_USER = gql`
mutation signIn($email: String!, $password: String!) {
signIn(email: $email, password: $password)
}
`;
// for example, my server address
const API_URI='https://xxxx.herokuapp.com/api';
const cache = new InMemoryCache();
const client = new ApolloClient({
uri: API_URI,
cache: new InMemoryCache()
});
const AuthContext = React.createContext();
function SplashScreen() {
return (
<View>
<Text>Loading...</Text>
</View>
);
}
function HomeScreen() {
const { signOut } = React.useContext(AuthContext);
return (
<View>
<Text>Signed in!</Text>
<Button title="Sign out" onPress={signOut} />
</View>
);
}
function SignInScreen() {
const [username, setUsername] = React.useState('');
const [password, setPassword] = React.useState('');
const { signIn } = React.useContext(AuthContext);
return (
<View>
<TextInput
placeholder="Username"
value={username}
onChangeText={setUsername}
/>
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button title="Sign in" onPress={() => signIn({ username, password })} />
</View>
);
}
const Stack = createStackNavigator();
export default function App({ navigation }) {
const [state, dispatch] = React.useReducer(
(prevState, action) => {
switch (action.type) {
case 'RESTORE_TOKEN':
return {
...prevState,
userToken: action.token,
isLoading: false,
};
case 'SIGN_IN':
return {
...prevState,
isSignout: false,
userToken: action.token,
};
case 'SIGN_OUT':
return {
...prevState,
isSignout: true,
userToken: null,
};
}
},
{
isLoading: true,
isSignout: false,
userToken: null,
}
);
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const bootstrapAsync = async () => {
let userToken;
try {
// Restore token stored in `SecureStore` or any other encrypted storage
// userToken = await SecureStore.getItemAsync('userToken');
} catch (e) {
// Restoring token failed
}
// After restoring token, we may need to validate it in production apps
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away.
dispatch({ type: 'RESTORE_TOKEN', token: userToken });
};
bootstrapAsync();
}, []);
const authContext = React.useMemo(
() => ({
signIn: async ({username, password}) => {
useMutation(SIGNIN_USER, {
variables:{
email: username,
password: password
},
onCompleted: data => {
console.log(data);
}
});
// In a production app, we need to send some data (usually username, password) to server and get a token
// We will also need to handle errors if sign in failed
// After getting token, we need to persist the token using `SecureStore` or any other encrypted storage
// In the example, we'll use a dummy token
dispatch({ type: 'SIGN_IN', token: data.signIn });
},
signOut: () => dispatch({ type: 'SIGN_OUT' }),
signUp: async (data) => {
// In a production app, we need to send user data to server and get a token
// We will also need to handle errors if sign up failed
// After getting token, we need to persist the token using `SecureStore` or any other encrypted storage
// In the example, we'll use a dummy token
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
},
}),
[]
);
return (
<AuthContext.Provider value={authContext}>
<ApolloProvider client={client}>
<NavigationContainer>
<Stack.Navigator>
{state.isLoading ? (
// We haven't finished checking for the token yet
<Stack.Screen name="Splash" component={SplashScreen} />
) : state.userToken == null ? (
// No token found, user isn't signed in
<Stack.Screen
name="SignIn"
component={SignInScreen}
options={{
title: 'Sign in',
// When logging out, a pop animation feels intuitive
animationTypeForReplace: state.isSignout ? 'pop' : 'push',
}}
/>
) : (
// User is signed in
<Stack.Screen name="Home" component={HomeScreen} />
)}
</Stack.Navigator>
</NavigationContainer>
</ApolloProvider>
</AuthContext.Provider>
);
}
And I get the error,
WARN Possible Unhandled Promise Rejection (id: 0):
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
I searched it, it is because I use hooks in useMemo.
https://reactjs.org/warnings/invalid-hook-call-warning.html
To avoid confusion, it’s not supported to call Hooks in other cases:
🔴 Do not call Hooks in class components.
🔴 Do not call in event handlers.
🔴 Do not call Hooks inside functions passed to useMemo, useReducer, or useEffect.
But, I have to send some data to server to get a token, and save token by Securestore or something else, If I can't write this section here, where?
I have no idea about where to write this section of code. My understanding to Redux in this situation:
when a user click button of 'sign in', call the function of signIn which is declared in authContext, useMemo, in use useMemo, get the token, save the token, then dispatch to notify data change, then go to the reducer.
After read a basic tutorial of redux, I have finished that by:
In SignInScreen, get the web token by 'login' function, which is triggered by the button of 'Sign in'
After execute it successfully, invoke the 'signIn' to update the state(by call dispatch).
I was confused with the name of 'signIn' function before, which is the same with my eventHandler function of 'signIn' button in SignInScreen. Actually, they are in duty of different logic.
import * as React from 'react';
import { Button, Text, TextInput, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { useMutation, gql } from '#apollo/client';
import {
ApolloClient,
ApolloProvider,
createHttpLink,
InMemoryCache
} from '#apollo/client';
import { setContext } from 'apollo-link-context';
const SIGNIN_USER = gql`
mutation signIn($email: String!, $password: String!) {
signIn(email: $email, password: $password)
}
`;
const API_URI='https://jseverywhere.herokuapp.com/api';
const cache = new InMemoryCache();
const client = new ApolloClient({
uri: API_URI,
cache: new InMemoryCache()
});
const AuthContext = React.createContext();
function SplashScreen() {
return (
<View>
<Text>Loading...</Text>
</View>
);
}
function HomeScreen() {
const { signOut } = React.useContext(AuthContext);
return (
<View>
<Text>Signed in!</Text>
<Button title="Sign out" onPress={signOut} />
</View>
);
}
function SignInScreen() {
const [username, setUsername] = React.useState('');
const [password, setPassword] = React.useState('');
const { signIn } = React.useContext(AuthContext);
const [logIn, {loading, error}] = useMutation(SIGNIN_USER, {
variables:{
email: username,
password: password
},
onCompleted: data => {
console.log(data);
signIn(data);
}
});
if(loading)
return <Text>Loading...</Text>;
if(error)
return <Text>Error--{error.message}</Text>;
return (
<View>
<TextInput
placeholder="Username"
value={username}
onChangeText={setUsername}
/>
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button title="Sign in" onPress={() => logIn()} />
</View>
);
}
const Stack = createStackNavigator();
export default function App({ navigation }) {
const [state, dispatch] = React.useReducer(
(prevState, action) => {
switch (action.type) {
case 'RESTORE_TOKEN':
return {
...prevState,
userToken: action.token,
isLoading: false,
};
case 'SIGN_IN':
return {
...prevState,
isSignout: false,
userToken: action.token,
};
case 'SIGN_OUT':
return {
...prevState,
isSignout: true,
userToken: null,
};
}
},
{
isLoading: true,
isSignout: false,
userToken: null,
}
);
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const bootstrapAsync = async () => {
let userToken;
try {
// Restore token stored in `SecureStore` or any other encrypted storage
// userToken = await SecureStore.getItemAsync('userToken');
} catch (e) {
// Restoring token failed
}
// After restoring token, we may need to validate it in production apps
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away.
dispatch({ type: 'RESTORE_TOKEN', token: userToken });
};
bootstrapAsync();
}, []);
const authContext = React.useMemo(
() => ({
signIn: async (data) => {
// In a production app, we need to send some data (usually username, password) to server and get a token
// We will also need to handle errors if sign in failed
// After getting token, we need to persist the token using `SecureStore` or any other encrypted storage
// In the example, we'll use a dummy token
dispatch({ type: 'SIGN_IN', token: data.signIn });
},
signOut: () => dispatch({ type: 'SIGN_OUT' }),
signUp: async (data) => {
// In a production app, we need to send user data to server and get a token
// We will also need to handle errors if sign up failed
// After getting token, we need to persist the token using `SecureStore` or any other encrypted storage
// In the example, we'll use a dummy token
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
},
}),
[]
);
return (
<AuthContext.Provider value={authContext}>
<ApolloProvider client={client}>
<NavigationContainer>
<Stack.Navigator>
{state.isLoading ? (
// We haven't finished checking for the token yet
<Stack.Screen name="Splash" component={SplashScreen} />
) : state.userToken == null ? (
// No token found, user isn't signed in
<Stack.Screen
name="SignIn"
component={SignInScreen}
options={{
title: 'Sign in',
// When logging out, a pop animation feels intuitive
animationTypeForReplace: state.isSignout ? 'pop' : 'push',
}}
/>
) : (
// User is signed in
<Stack.Screen name="Home" component={HomeScreen} />
)}
</Stack.Navigator>
</NavigationContainer>
</ApolloProvider>
</AuthContext.Provider>
);
}

Reload 2 times useEffect - React Native and SQLite (expo)

I'm new to react native, so I apologize for errors in the code. First: I use Android Studio, VS Code, react native(expo) and sqlite(expo), supporting only android (I haven't programmed for ios yet).
I have a serious problem in my code and I have no idea how to start. The 'useEffect', to work, you have to save the file 2 times (ctrl+s in vs code). Don't just open the app.
THIS IS MY LOGIN PAGE login.js
import React, {useState,useEffect} from 'react';
import {View,Text,Pressable,Image,FlatList,ImageBackground,Alert} from 'react-native';
import {TextInput} from 'react-native-gesture-handler';
import fundoLogin from './styleLogin';
import Tb_user from '../../src/models/tb_user';
import Tb_veic_user from '../../src/models/tb_veic_user';
import db from "../../src/services/SQLiteDatabse";
import NetInfo from '#react-native-community/netinfo';
const printUser = (user) => {
console.log(`ID_user:${user.ID_user}, username:${user.username}, senha:${user.senha}, ID_empresa:${user.ID_empresa}, nome_empresa:${user.nome_empresa}, status_user:${user.status_user}`)
}
const printV = (v) => {
console.log(`ID_veic_uso:${v.ID_veic_uso}, username:${v.username}, placa:${v.placa}, atividade:${v.atividade}, data_inic:${v.data_inic}, km_inic:${v.km_inic}, data_final:${v.data_final}, km_final:${v.km_final}, obs:${v.obs}, integrado:${v.integrado}`)
}
function Login({ navigation, route }) {
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const [errorLogin, setErrorLogin] = useState("")
const [users, setUsers] = useState([])
const [net, setNet] = useState("")
const [dadosVeic, setDadosVeic] = useState([])
const [info, setInfo] = useState([])
const findTbVeic = (id) => {
return new Promise((resolve, reject) => {
db.transaction((tx) => {
tx.executeSql(
"SELECT * FROM tb_veic_user WHERE integrado=0 AND km_final IS NOT NULL ;",
[],
//-----------------------
(_, { rows }) => {
if (rows.length > 0) {
resolve(rows._array)
}
else {
reject("Obj not found: id=" + id);
} // nenhum registro encontrado
},
(_, error) => reject(error) // erro interno em tx.executeSql
);
});
});
};
useEffect(() => {
NetInfo.fetch().then(state => {
setNet(state.isConnected)
});
if (net === true) {
findTbVeic()
.then(a => setDadosVeic(a))
.then( dadosVeic.map(item => {
// ENVIA DADOS PARA A API
fetch('url API', {
method: 'POST',
body: JSON.stringify({
username: item.username,
placa: item.placa,
atividade: item.atividade,
data_inic: item.data_inic,
km_inic: item.km_inic,
data_final: item.data_final,
km_final: item.km_final
}),
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
})
//ALTERA O 'INTEGRADO' == 1
Tb_veic_user.updateV( item.ID_veic_uso )
}))
}else{
console.log('sem conexao com a internet')
}
},[])
const importTbUser = () => {
// DADOS DA API SALVA EM CONST USERS
fetch('url API')
.then( res => res.json())
.then(
(dado) => {
setUsers(dado.data)
}
)
// INSERE, NO BANCO DE DADOS, OS DADOS DA API
const a = users.map((element) => {
Tb_user.create({ID_user:element.ID_user, username:element.username, senha:element.senha, ID_empresa:element.ID_empresa, nome_empresa:element.nome_empresa, status_user:element.status_user})
});
}
const login = () => {
// FAZER LOGIN
return new Promise((resolve, reject) => {
db.transaction(tx => {
tx.executeSql(
'SELECT ID_user, username, senha FROM tb_user WHERE username = ? and senha = ? LIMIT 1;',
[username, password],
//-----------------------
(_, { rows }) => {
if (rows.length > 0){
resolve(rows._array),
navigation.navigate('Veic', {
paramKey: username,
})
}else{
reject("Obj not found");
} // nenhum registro encontrado
},
(_, error) => reject(error) // Success
);
});
});
}
return (
<View style={fundoLogin.container}>
<ImageBackground
source={require('../../imagens/1.jpg')}
style={fundoLogin.imageFundo}
>
<Image
style={fundoLogin.imageLogo}
source={require('../../imagens/logo_cymi.png')}
/>
{net === true ?
<Image
style={{height:30,width:30}}
source={require('../../imagens/signal.png')}
/>
:
<Image
style={{height:30,width:30}}
source={require('../../imagens/no-signal.png')}
/>
}
<TextInput
style={fundoLogin.input}
placeholder='Digite seu email'
onChangeText={txtUsername => setUsername(txtUsername)}
value={username}
/>
<TextInput
style={fundoLogin.input}
placeholder='Digite sua senha'
secureTextEntry={true}
onChangeText={txtPassword => setPassword(txtPassword)}
value={password}
/>
{errorLogin === true
?
<View>
<Text style={fundoLogin.error}>email ou senha inválido</Text>
</View>
:
<View>
</View>
}
{(username === "" || password === "")
?
<Pressable
disabled={true}
style={fundoLogin.button}
>
<Text style={fundoLogin.textButton}>Login</Text>
</Pressable>
:
<Pressable
style={fundoLogin.button}
onPress={login}
>
<Text style={fundoLogin.textButton}>Login</Text>
</Pressable>
}
<Pressable
style={fundoLogin.button}
onPress={importTbUser}
>
<Text style={fundoLogin.textButton}>Importar</Text>
</Pressable>
<Pressable
style={fundoLogin.info}
onPress={navigation.navigate('Info')}
>
<Text style={fundoLogin.textInfo}>?</Text>
</Pressable>
</ImageBackground>
</View>
);
}
export default Login;
the app, when opened, will search (if it has internet) in the database for vehicles that have a final kilometer (which have already been selected and released), and will add this information inside the api, updating the database (changing the ' integrated to 1', that is, informing that aql "file" has already been to api)
My app works like this: it starts on the login page (where there is a login button, import button and a company information button) and goes to another page to select a vehicle (this 'veic' page also has the same problem in useEffect). All components are working perfectly fine, I've checked all possible console.logs, and it's running fine. The only problem is that use effect doesn't work (on this page it has to be run 2x to refresh, on the 'veic' page it only does one thing, and the others don't). If anyone can tell me more, please. I don't even know if it's an error in my code or in Android Studio. Thank you in advance!!!
The problem is that you are not awaiting NetInfo.fetch().
Write the code under NetInfo.fetch() in NetInfo.fetch().then(... your remaining useEffect code...).
Like this:
useEffect(() => {
NetInfo.fetch().then((state) => {
if (state.isConnected) {
findTbVeic()
.then(a => setDadosVeic(a))
.then( dadosVeic.map(item => {
// ENVIA DADOS PARA A API
fetch('url API', {
method: 'POST',
body: JSON.stringify({
username: item.username,
placa: item.placa,
atividade: item.atividade,
data_inic: item.data_inic,
km_inic: item.km_inic,
data_final: item.data_final,
km_final: item.km_final
}),
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
})
//ALTERA O 'INTEGRADO' == 1
Tb_veic_user.updateV( item.ID_veic_uso )
}))
}else{
console.log('sem conexao com a internet')
}
});
}, []);
For your other useEffect: The problem is that you can't change a state and than use that state in an useEffect drectly after. This is cause of the execution of the useEffect. It will only rerender when variables in the useEffect array change. For example:
const Test = () => {
const [test, setTest] = useState(false);
useEffect(() => {
fetchTest().then((data) => {
// lets say data.test is "true"
setTest(data.test); // set to true
// states in useEffect will keep the value they had when the useEffect started
// DONT:
console.log("test:", test): // --> "test: false"
// DO
console.log("test:", data.test): // --> "test: true"
});
}, []);
}
I understood what Maximilan said, and on that login page it worked really well. But on the 'veic' page I'm not so lucky
useEffect(() => {
findByUserna( route.params.paramKey )
.then(
findByV(route.params.paramKey)
.then(
d => setDados(d)
),
console.log('use effect 1 ------>',dados)
)
.then(
dados.map(
item => setIDVeic(item.ID_veic_uso)
),
console.log('use effect 2 ------>',IDVeic)
)
},[])
When opening the screen, the consoles are empty, the variable is not saved, but when updating, it works, the variables are saved and shown in the console normally:
//The first time, console:
use effect 1 ------> Array []
use effect 2 ------> null
ENCONTRADO de tb_user o ID_user, username, nome_empresa ---> SORAIA
ENCONTRADO de tb_veic_user o ID_veic_uso onde kmf=null
//The second time, console:
use effect 1 ------> Array [
Object {
"ID_veic_uso": 4,
},
Object {
"ID_veic_uso": 7,
},
]
use effect 2 ------> 7

How to navigate in const function [react native]

I have 2 questions:
How do I add props to this function in order to navigate
const usersRef = firestore().collection('Users');
//const signIn = React.useContext(AuthContext);
const { signIn } = React.useContext(AuthContext);
const CreateUser = async (email, password) => {
try {
let response = await auth().createUserWithEmailAndPassword(email, password)
if (response) {
console.log( "?", response)
}
} catch (e) {
console.error(e.message)
}
usersRef.add({
// Name: this.state.Name,
Email: email
})
navigation.navigate("SignIn")
}
export function RegisterScreen({navigation}) {
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
const { register } = React.useContext(AuthContext);
const {container, txtInput} = styles;
return (
<View style={container}>
<Text>Reg</Text>
<TextInput
placeholder="email"
value={email}
onChangeText={setEmail}
style={txtInput}
/>
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
style={txtInput}
/>
<Button title="Register" onPress={() => {
// navigation.navigate('signIn')
//register(email,password)
CreateUser(email,password)
}} />
</View>
);
}
I want to make it logged in after the registration is complete, so far as my understanding I need to get it to SignIn function of my AuthContext
I have const signIn = React.useContext(AuthContext);
and I try to do signIn(email,password) after or before the navigation.
please try this, where did you put it CreateUser
const { signIn } = React.useContext(AuthContext);
const data = {
username: state.username,
password: state.password
}
signIn(data)
App.js
it will switch automatically
{userToken ?
<RootStack.Navigator>
<RootStack.screen />
</RootStack.Navigator> :
<RootStack.Navigator>
<RootStack.screen />
</RootStack.Navigator>}
You can change routes by doing this
const CreateUser = async (data,navigation) => {
try {
let response = await auth().createUserWithEmailAndPassword(data.email, data.password)
if (response) {
console.log("test")
}
} catch (e) {
console.error(e.message)
}
usersRef.add({
// Name: this.state.Name,
Email: email
})
navigation.navigate("SignIn")
}
So I fixed it by changing registration to class then I was able to use navigation and using constructor props.
The only problem is that the auth context doesnt seem to support class. so I navigate after registration to signIn that remained as function so there Im able to use the authContext SignIn
If someone want to help me and tell me if its possible to manage it on class instead of function it would be greatful.

How do I get this handleSubmit on a Formik form to perform a Post request

My handleSubmit function on a Formik form is not properly sending a Post request to my backend API. I believe I have the code 97% of the way there, but don't quite understand the nuances of what is missing.
The form has an email field, and I need the Submit to pass that email string to a POST request. The code below has 4 files at play: Enter-Email.js, SessionStore.js, UserService.js and request.js.
I believe most of the problem is in my handleSubmit function in Enter-Email.js and my #action sendResetCode in SessionStore.js.
Thanks for any assistance.
Enter-Email.js:
import React, { Component, Fragment } from "react";
import { observer } from "mobx-react/native";
import { Formik } from "formik";
import * as yup from "yup";
#observer
class EnterEmail extends Component {
handleSubmit = async ({ <----- function I think has the problem
email,
values,
}) => {
const {
navigation: { navigate },
sessionStore: { sendResetCode }
} = this.props;
let response = await sendResetCode({ <------ the function to make the POST request!
email: email
});
if (response) {
return navigate("VerificationCode", { values });
} else {
return Alert(
{ message: "Error sending the reset link" },
"There was an error sending the reset link. Please try again."
);
}
}
render() {
return (
<SafeAreaView style={{ flex: 1 }}>
<Text>Enter your e-mail to receive a link to reset your password</Text>
<Formik
initialValues={{ email: ''}}
onSubmit={this.handleSubmit}
validationSchema={yup.object().shape({
email: yup
.string()
.email()
.required(),
})}
>
{({ values, handleChange, setFieldTouched, handleSubmit }) => (
<Fragment>
<View>
<View>
<TextInputGroup
id="email"
keyboardType="email-address"
label="Email"
onBlur={() => setFieldTouched("email")}
value={values.email}
onChangeText={handleChange("email")}
/>
</View>
</View>
<View>
<LargeButton
text="Send Reset Link"
onPress={handleSubmit}
/>
</View>
</Fragment>
)}
</Formik>
</SafeAreaView>
);
}
}
export default EnterEmail;
SessionStore.js:
#action sendResetCode = async (emailField: Object): Promise<void> => {
let res = await userService.sendResetCode(emailField);
// if it fails, return res.success to trigger error on enter-email.js
if (!res.success) {
return res.success;
}
};
UserService.js:
// to send code to email that was entered:
userService.sendResetCode = async (
body: Object
): Promise<ApiResponse<Nurse>> => {
const response: Response = await request.post(
`/v1/password_resets`, <---- backend endpoint which works
body
);
return handleResponse(response);
};
request.js:
post(url, body, useBody) {
return fetch(this._buildUrl(url), {
method: "POST",
headers: getCommonHeaders(useBody),
body: useBody ? body : JSON.stringify(body)
});
},
In case it matters, here is the versions in Package.json:
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
"native-base": "^2.12.1",
"react": "16.8.0",
"react-navigation": "^3.11.0",
"formik": "^1.5.8",
"mobx": "5.9.4",
"mobx-react": "5.4.4",

Why isn't mailchimp API working with fetch?

I'm trying to add an email address to a mailchimp list I have.
This is for a react native app and I'm trying to implement the request using fetch.
This is my code within the component:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
import { emailChanged, nameChanged, addToWaitingList } from '../actions';
import { Card, CardSection, Input, Button, Spinner } from '../components/Auth';
class addToWaitingListForm extends Component {
onEmailChange(text) {
this.props.emailChanged(text);
}
onButtonPress() {
const { email } = this.props;
this.props.addToWaitingList({ email });
}
renderButton() {
if (this.props.loading) {
return <Spinner size="large" />;
}
return (
<Button onPress={this.onButtonPress.bind(this)}>
Keep me in the loop!
</Button>
);
}
render() {
return (
<View>
<Card>
<CardSection>
<Input
placeholder="your name"
onChangeText={this.onNameChange.bind(this)}
value={this.props.name}
/>
</CardSection>
<CardSection>
<Input
placeholder="email#uni.ac.uk"
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
/>
</CardSection>
<Text style={styles.errorTextStyle}>
{this.props.error}
</Text>
<CardSection style={{ borderBottomWidth: 0 }}>
{this.renderButton()}
</CardSection>
</Card>
</View>
);
}
}
const mapStateToProps = ({ auth }) => {
const { email, name, error, loading } = auth;
return { email, name, error, loading };
};
export default connect(mapStateToProps, {
emailChanged,
addToWaitingList
})(addToWaitingListForm);
Add this is my action code for interacting with the mailchimp api:
import Router from '../../navigation/Router';
import { getNavigationContext } from '../../navigation/NavigationContext';
export const addToWaitingList = ({ email }) => {
const emailListID = 'e100c8fe03';
fetch(`https://us13.api.mailchimp.com/3.0/lists/${emailListID}/members/`, {
method: 'POST',
body: JSON.stringify({
'email_address': email,
'status': 'subscribed',
'merge_fields': {
'FNAME': 'Urist',
'LNAME': 'McVankab'
}
})
})
.then(() => addSubscriberSuccess())
.catch(error => console.log(error));
};
const addSubscriberSuccess = () => {
getNavigationContext().getNavigator('root').immediatelyResetStack([Router.getRoute('auth')]);
};
Right now, the error I'm just getting back is ExceptionsManager.js:62 Cannot read property 'type' of undefined and Error: unsupported BodyInit type
What does this mean and how can I fix this?
You need to do two things.
First off you need to send the basic authentication via fetch so you cant do "user:pass" You have to convert it with btoa('user:pass').
Then you have to send it with mode: 'no-cors'
let authenticationString = btoa('randomstring:ap-keyxxxxxxx-us9');
authenticationString = "Basic " + authenticationString;
fetch('https://us9.api.mailchimp.com/3.0/lists/111111/members', {
mode: 'no-cors',
method: 'POST',
headers: {
'authorization': authenticationString,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email_address: "dude#gmail.com",
status: "subscribed",
})
}).then(function(e){
console.log("fetch finished")
}).catch(function(e){
console.log("fetch error");
})