I would like to use a GraphQL API on a React-Native App with react-apollo.
My server-side API is functional. All my tests with the playground works perfectly.
On the other hand, since react-native things get complicated: how to reuse what was set up server side? (mutation in particular) Do I have to duplicate the GraphQL code?
I exported my schema graphql using CodeGen (great tool!), But how to use it on react-native ?
My config on React-Native :
const httpLink = createHttpLink({
uri: API_GRAPHQL_URL,
clientState: {
typeDefs
}
});
const getToken = async () => await AsyncStorage.getItem('userToken');
const authLink = setContext(async (_, { headers }) => {
const token = await getToken();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : ''
}
};
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
My typeDefs is schema exported on CodeGen, for example :
But, how use Mutation with my exported configuration ? I use react-apollo-hooks.
The client part of GraphQL is not very clear for me, despite a great reaction-apollo documentation.
Would anyone help me or have a reference article on the subject?
Thank you so much !
in order to reuse you can create a schema.js file and export your queries from there in to to relevant screens, with regards to Mutations here is an example of a signUp page that uses Mutation.
import FormInput from '../components/FormInput';
import FormButton from '../components/FormButton';
import { useMutation } from '#apollo/client';
import { gql } from 'apollo-boost'
const SIGNUP_USER = gql`
mutation SignupMutation($email: String!, $password: String!, $name: String!) {
signupWithEmail(email: $email, password: $password, name: $name) {
user {
email
name
}
}
}`
function SignUp (){
export default function SignUp() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [signupWithEmail] = useMutation(SIGNUP_USER);
const navigation = useNavigation();
const navigation = useNavigation();
return (
<ScrollView>
<View style={{justifyContent: 'center', alignItems: 'center'}}>
<FormInput
style={styles.input}
maxLength={15}
placeholder="name"
onChangeText={name => setName(name)}
value={name}
/>
<FormInput
style={styles.input}
placeholder="email"
onChangeText={email => setEmail(email)}
value={email}
autoCorrect={false}
keyboardType='email-address'
autoCapitalize='none'
/>
<FormInput
style={styles.input}
maxLength={15}
secureTextEntry={true}
placeholder="password"
onChangeText={password => setPassword(password)}
value={password}
/>
<FormButton
title="Signup"
modeValue="contained"
color="#2D374F"
labelStyle={styles.loginButtonLabel}
onPress={() => signupWithEmail({variables: {email, password, name}})}
/>
</View>
</ScrollView>
);
}
your server side resolver should look like this please note im using firebase here.
Mutation: {
signupWithEmail: async (_, { email, password }) => {
const user = firebase.auth()
.createUserWithEmailAndPassword(email, password)
.then((userCredentials) => { return userCredentials.user.updateProfile
({displayName: name})
})
return { user }
},
},
and your schema should look like this:
type Mutation {
signupWithEmail(email: String!, password: String!, name: String!): AuthPayload!
loginWithEmail(email: String!, password: String!): AuthPayload!
}
type User {
uid : ID!
email: String!
name: String!
}
type AuthPayload {
user: User!
}
Related
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>
);
}
enter image description here
Above is the image description of what I want to achieve. I already implemented all the layers but I have a problem in automatically updating userContext. I have to refresh the userContext.js page manually to all the information across the app get updated as I want.
My DataBase.js looks like this
import React, { createContext, useState, useEffect,useCallback} from "react";
// create context
const UserContext = createContext();
const UserContextProvider = ({children}) => {
// the value that will be given to the context
var data = global.userSignedUp
const [UserDetails, SetUserDetails] = useState(data)
const [loadingUser, setLoadingUser] = useState(true);
// fetch a user from a fake backend API
const fetchUser = async () => {
// this would usually be your own backend, or localStorage
// for example
var name = global.userSignedUp.username
var index= global.userSignedUp.id
console.log('This the response from DataBase.js',UserDetails)
const data= await fetch('XXXXXX/api/userprofile/'+UserDetails.id+'/',{
method : 'GET',
header : {
Accept : 'application/json',
'Content-Type' : 'application/json'
}
}).then(async res =>{
const response =await res.json()
console.log('This the response from DataBase.js',response)
SetUserDetails(response)
})
//
};
useEffect(() => {
fetchUser();
}, []);
return (
// the Provider gives access to the context to its children
<UserContext.Provider value={[UserDetails, SetUserDetails]}>
{children }
</UserContext.Provider>
);
};
export { UserContext, UserContextProvider };
I defined one global variable index that get updated depending on the user ID from the sign in page and passed inside the Database.js
const onSignInPressed =(logindata)=>{
const username = logindata.username
const password =logindata.password
// state={
// credentials :{username : username, password: password}
// }
fetch('http://XXXXX/auth/',{
method:'POST',
headers:{'Content-Type':'application/json'},
body : JSON.stringify({username:username, password:password})
}).then (async data =>{
const DataResponse =await data.json()
//console.log(DataResponse)
if (data.status===200){
fetch('http://XXXX/api/userprofile',{
method : 'GET',
header : {
Accept : 'application/json',
'Content-Type' : 'application/json'
}
}).then(async res =>{
const response =await res.json()
for (let i=0; i < response.length;i++){
if (response[i].user=== username){
userSignedUp=response[i]
console.log('this is the user Signed up Id',userSignedUp)
navigation.navigate('HomeProfile')
;
}
}
})
}
else{
Alert.alert('Invalid User!','Username or password is incorrect,')
}
})
}
in my HomeProfile.js I can get all the data from UserContext and it workds fine.
Now in my EditProfile.js I have this :
import React , {useState,useContext,useEffect,useCallback} from 'react'
import {SocialIcon} from 'react-native-elements'
import { ImageBackground, View, Text, Image, StyleSheet, useWindowDimensions,Alert} from 'react-native'
import { icons,images, COLORS, SIZES } from '../constants';
import {CustomInput} from '../Component/CustomInput';
import CustomButton from '../Component/CustomButton';
import {useNavigation} from '#react-navigation/native';
import {useForm, Controller} from 'react-hook-form';
import { UserContext, UserContextProvider } from "./MobileDatabase/DataBase";
const EditProfile = () => {
return (
<UserContextProvider>
<EditProfilePage />
</UserContextProvider>
);
}
const EditProfilePage = () => {
const {control,handleSubmit,watch} = useForm();
const {height}= useWindowDimensions();
const navigation =useNavigation();
const newpwd = watch('NewPassword')
const [UserDetails, SetUserDetails]= useContext(UserContext);
let OldUsername=UserDetails.user
let DataFromServer=UserDetails
const onSavePressed = async (EditData) =>{
console.log(EditData)
const uploadData =new FormData();
uploadData.append('username',EditData.ChangeUsername)
let editData= await fetch('http://192.168.56.1:8000/api/users/'+OldUsername+'/',{
method:'PATCH',
headers:{Accept: 'application/json',
'Content-Type': 'multipart/form-data; '},
body :uploadData,
})
let EditDataResponseJson = await editData.json();
console.log(EditDataResponseJson)
SetUserDetails({...UserDetails, user:EditDataResponseJson.username})
UserContextProvider(EditDataResponseJson)
navigation.navigate('HomeProfile')
}
return (
<View style={styles.root}>
<Image
source ={{uri:UserDetails.Profileimage}}
style={[styles.logo]}
//resizeMode='contain'
/>
<Text style={{fontWeight:'bold',
//position:"absolute",
flex:1,
fontSize: 20,
top:'3%',
maxWidth:200,
alignSelf:'center' }}>{UserDetails.user}</Text>
<CustomInput placeholder ='Change UserName'
name="ChangeUsername"
control={control}
rules={{required: 'Username is required',
minLength:{
value:3,
message :'Username should be at least 3 characters long'
}
}}
// value={username}
// setValue={setUsername}
/>
<CustomInput placeholder= 'Change Password'
name ='NewPassword'
control={control}
rules={{required: 'New password is required'}}
// value={password}
// setValue={setPassword}
secureTextEntry={true}
/>
<CustomInput placeholder= 'Confirm Password'
name ='RepeatNewPassword'
control={control}
rules={{
validate: value => value === newpwd || 'Password do not match'
}}
// value={password}
// setValue={setPassword}
secureTextEntry={true}
/>
<View style ={styles.Signup}>
<CustomButton text= 'Save Changes' onPress={handleSubmit(onSavePressed)} />
</View>
</View>
)
}
const styles = StyleSheet.create({
root :{
//alignItems :'center',
paddingTop :'60%',
flex: 1,
},
cover:{
flex:1,
justifyContent: 'center',
},
logo:{
position :'absolute',
top:'10%',
alignSelf:'center',
justifyContent:'center',
alignItems: 'center',
width: 150,
height: 150,
borderRadius: 400/ 2,
},
Signup:{
marginTop: '20%',
}
})
export default EditProfile
When I update the Username for example, it only changes inside EditPage.js but not in HomeProfile.js event thought I update the SetUserDetails. The only way to see the updated data is to refresh the Database.js.
I am very much stuck since a month now and hope for a help here.
Thank you!!
I am struggling with act errors when it comes to testing my React Native application using JEST and testing-library.
I have a simple Formik form and I am trying to test if the validation works.
My screen I am testing:
const SignInScreen: React.FC = () => {
const { translations } = useContext(LocalizationContext);
const [signIn, { isLoading, isError }] = useSignInMutation();
const initialValues: SignInRequest = {
name: '',
password: ''
};
const validationSchema = Yup.object({
name: Yup.string()
.required(translations['required'])
.max(15, ({max}) => translations.formatString(
translations['validationNCharOrLess'], { n: max })),
password: Yup.string()
.required(translations['required'])
});
const handleSubmit = async (values: SignInRequest, formikHelpers: FormikHelpers<SignInRequest>) => {
await signIn(values)
.unwrap()
.catch(e => {
if ('data' in e && e.data &&
'errors' in e.data && e.data.errors)
{
formikHelpers.setErrors(mapErrors(e.data.errors));
}
})
}
return (
<SafeAreaView
testID={tiConfig.SAFE_AREA_VIEW}
style={{ flex: 1 }}>
<View
testID={tiConfig.SIGN_IN_SCREEN}
style={styles.container}>
<View>
<Text>{translations['signIn']}</Text>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}>
{
({ values, errors, handleSubmit, handleChange }) => (
<View>
<Input
testID={tiConfig.SIGN_IN_USERNAME_INPUT}
value={values.name}
placeholder={translations['username']}
onChangeText={handleChange('name')}
errorMessage={errors.name} />
<Input
testID={tiConfig.SIGN_IN_PASSWORD_INPUT}
value={values.password}
placeholder={translations['password']}
onChangeText={handleChange('password')}
errorMessage={errors.password}
secureTextEntry />
{
isError ?
<View>
<Text testID={tiConfig.SIGN_IN_SERVER_ERROR}>
{ translations['somethingWentWrongTryAgainLater'] }
</Text>
</View>
: null
}
<Button
testID={tiConfig.SIGN_IN_SUBMIT}
title={translations['signIn']}
onPress={handleSubmit}
loading={isLoading} />
</View>
)
}
</Formik>
</View>
</View>
</SafeAreaView>
);
}
My test:
// tiConfig is a json with my test id constants
test.only("Sign in username field validates correctly", async () => {
const component = render(<SignInScreen />);
const usernameInput = await component.findByTestId(tiConfig.SIGN_IN_USERNAME_INPUT);
// A bit weird way to find the error text with a nesting but it works for now
const errorMessage = usernameInput
.parent!.parent!.parent!.parent!.parent!.parent!.findByType(Text);
const submit = component.getByTestId(tiConfig.SIGN_IN_SUBMIT);
fireEvent.press(submit);
await waitFor(() => expect(errorMessage.props.children).toBe(translations.required));
fireEvent.changeText(usernameInput, "username");
await waitFor(() => expect(errorMessage).toBeEmpty());
fireEvent.changeText(usernameInput, "toolongusernameshouldntbeallowed");
await waitFor(() => expect(errorMessage).not.toBeEmpty());
});
Warning:
Warning: You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);
at registerError (node_modules/react-native/Libraries/LogBox/LogBox.js:172:15)
at errorImpl (node_modules/react-native/Libraries/LogBox/LogBox.js:58:22)
at console.Object.<anonymous>.console.error (node_modules/react-native/Libraries/LogBox/LogBox.js:32:14)
at printWarning (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:68:30)
at error (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:44:5)
at node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15297:13
at tryCallOne (node_modules/promise/lib/core.js:37:12)
I get this warning 3 times
Without waitFor my test doesn't pass as all of the expect need to be awaited. I tried to wrap fireEvents in act as well, but according to few blog posts from Kent C. Dodds we shouldn't wrap fireEvent in act so although the test passes I still get the warnings.
Any ideas how I can fix this?
I faced similar issues. Wrapping fireEvent with await waitFor did the trick for me. So, when you call fireEvent.changeText make sure to wrap it with await waitFor
In your case,
test.only("Sign in username field validates correctly", async () => {
... Other test suites
await waitFor(() => {
fireEvent.changeText(usernameInput, 'username');
});
await waitFor(() => {
fireEvent.changeText(usernameInput, 'toolongusernameshouldntbeallowed');
});
});
Well, wrapping fireEvent in act actually solved the issue and I am not getting warnings, if has a different answer that would work or explanation why this work I would be delighted to hear it.
im looking for a bit of guideness here, im working on a RN app with redux and everytime i enter a new screen on the app, must likely i have a "callinitialData" function inside my useEffect(), using axios to fetch api data to be dispatch() to the redux state.
Everything works but whenever i jump from screen to screen to fast, sometimes i get a 429 error of to many request, so i just setup the redux-persist hoping that would help reduce the amount of request,in my mind thinking that if my api data is equal to my local data, that request wouldnt be necessary to be made.
However it stays the same so i was thinking what would be the best aproach here, on login try to fetch all the data at once > store it at asyncstorage and redux, and fetch that on each screen ?
how would i be able then, if i fetch all the data on login, receive the new data sets from the api in real time?
App functionality -
Edit Profile (img, pass, email, name)
Data Forms (requeast X, submit data, edit forms)
Chat by contacts / create Group chat
Code Example
const ChatScreen = ({ auth: { user }, getChatContacts, chat: { contacts }, navigation }) => {
useEffect(() => {
getChatContacts();
}, []);
const onChatUser = async (_id, name, roomID) => {
console.log(_id, name, roomID, contacts.payload.clone)
navigation.navigate( "Message", {
_id, name, chatRoomId: roomID, allUsers: contacts.payload.clone
});
}
const renderItem = ({ item , index } ) => {
let userName = "";
item.users.map((users, index) => {
let idToCheck = users.toString() !== user._id.toString() ? users : false;
if (idToCheck) {
let getOneUser = contacts.payload.clone.find(x => x._id === idToCheck);
userName += "" + getOneUser.name + ", ";
}
})
return (<TouchableOpacity key={item._id} onPress={() => onChatUser(item._id, item.name, item.roomID)}>
<View style={styles.chatContainer}>
<FontAwesome name="user-circle-o" size={50} color="#000000"/>
<Text style={styles.chatTitle}>{ ((userName).length > 32) ?
(((userName).substring(0,32-3)) + '...') :
userName }</Text>
<FontAwesome name="angle-right" size={25} color="#000000"/>
</View>
</TouchableOpacity>)
};
return (
<SafeAreaView style={styles.container}>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
placeholder="Search friend"
style={styles.chatsearch}
/>
{contacts ?
(<FlatList
data={contacts.payload.allContact}
renderItem={(item, index) => renderItem(item, index)}
keyExtractor={item => item.id}
style={styles.FlatListContainer}
/>) : (<Text style={styles.FlatListContainer}></Text>)
}
</SafeAreaView>
);
}
const styles = StyleSheet.create({});
ChatScreen.propTypes = {
isAuthenticated: PropTypes.bool,
auth: PropTypes.object,
};
const mapStateProps = state => ({
auth: state.auth,
chat: state.chat
});
export default connect(mapStateProps, {getChatContacts} )(ChatScreen);
Redux Action
export const getChatContacts = () => async dispatch => {
const config = {
header: {
"Content-Type": "application/json"
}
}
try {
const res = await axios.get(API_LINK +"/users/getChatContacts",);
dispatch({
type: GET_CONTACT_CHAT,
payload: res.data
});
} catch (err){
console.log(err)
dispatch({
type: ERROR_FAMILY_PARENT,
payload: { msg: err.response, status: err.response}
});
}
};
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.