I am building my first React Native app. I am in the process of making the automatic login with AsyncStorage. I already got the login page figured out and I managed to store the user in AsyncStorage. But in the
App.js
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import Login from './components/login'
import Signup from './components/signup'
import Home from './components/home'
import Profile from './components/profile';
const getData = async (user) => {
try {
const value = await AsyncStorage.getItem('#userauthkey')
if (value !== null) {
return value
} else {
return false
}
} catch (e) {
console.log(e)
}
}
const c = async () => {
const ch = await getData('#userauthkey')
return ch
}
console.log(c())
const AppNavigator = createStackNavigator({
Login: {screen: Login},
Signup: {screen: Signup},
Home: {screen: Home},
Profile: {screen: Profile},
})
const AppNavigator2 = createStackNavigator({
Home: {screen: Home},
Login: {screen: Login},
Signup: {screen: Signup},
Home: {screen: Home},
Profile: {screen: Profile},
})
const App = createAppContainer(c() == false ? AppNavigator : AppNavigator2)
export default App;
I get "Error: Objects are not valid as a React child (found: object with keys {_U, _V, _W, _X}). If you meant to render a collection of children, use an array instead." How can I fix it to make it sign the user in automatically?
login screen
import { StatusBar } from 'expo-status-bar'
import React, { useState, useEffect } from 'react'
import { TextInput } from 'react-native-gesture-handler';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { StyleSheet, Text, View, FlatList, Image, Button, Pressable, ScrollView } from 'react-native';
export default function Login(props) {
const message = props.navigation.getParam('message', null)
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const setStringValue = async (value, user) => {
try {
await AsyncStorage.setItem(user, value)
} catch (e) {
console.log(e)
}
}
const getData = async (user) => {
try {
const value = await AsyncStorage.getItem('#userauthkey')
if (value !== null) {
return value
} else {
return false
}
} catch (e) {
console.log(e)
}
}
const log = () => {
fetch(`http://192.168.5.223:8000/home/login/`, {
method: 'POST',
headers: {
"Content-Type": 'application/json'
},
body: JSON.stringify({ username: username, password: password }),
})
.then(res => res.json())
.then(async (res) => {
console.log(res)
if (res.valid === true) {
await setStringValue(username, '#userauthkey')
let ch = await getData(username)
console.log(ch)
if (res.set === true) {
props.navigation.navigate("Home", { "user": username })
} else {
props.navigation.navigate("Profile", { "user": username })
}
} else {
props.navigation.navigate("Login", { 'message': "username/password are incorrect" })
}
})
.catch(error => console.log(error))
}
const sign = () => {
props.navigation.navigate("Signup")
}
return (
<View style={styles.container} >
<ScrollView style={styles.scroll} >
<View style={styles.main}>
<Text style={styles.error}> {message} </Text>
< Text style={styles.label} >
Username:
</Text>
<TextInput style={styles.input} placeholder="Username"
onChangeText={text => setUsername(text)} value={username}
autoCapitalize={'none'}
/>
<Text style={styles.label}> Password: </Text>
<TextInput style={styles.input} placeholder="Password" onChangeText={text => setPassword(text)}
value={password} secureTextEntry={true} autoCapitalize={'none'}
/>
<Button onPress={() => log()} title="Login" > </Button>
</View>
</ScrollView>
< View style={styles.footer} >
<Button onPress={() => sign()} title="Don't have an acount? Sign up now" />
</View>
< StatusBar style="auto" />
</View>
)
}
Login.navigationOptions = screenProps => ({
headerLeft: () => null,
gestureEnabled: false,
headerStyle: {
backgroundColor: 'black'
},
headerTintColor: 'white',
})
const styles = StyleSheet.create({
error: {
textAlign: 'center',
color: 'red',
},
main: {
paddingTop: 145,
paddingHorizontal: 5,
},
container: {
flex: 1,
backgroundColor: 'black',
},
scroll: {
backgroundColor:'#'
},
footer: {
borderTopColor: '#fff',
padding:35,
},
label: {
fontSize: 24,
color: "white",
padding: 10,
},
input: {
fontSize: 24,
backgroundColor: "white",
padding: 10,
margin: 10,
borderRadius: 5,
},
});
Did you imported AsyncStorage in your app.js?
import AsyncStorage from '#react-native-async-storage/async-storage';
You used it but didn't import it, in the code shown. Try to import it and recompile
Related
I have managed to get my login authentification working, but I am having an issue with the conditional navigator in App.js. If I log in using the LoginStackNavigator, I then need to refresh the app to be able to use the DrawerNavigator. I would like it so that as soon as I log in, App.js realises this, and takes me to the drawer navigator.
I've tried to use the Context API but it isn't working. What am I doing wrong?
Here is my AppContext.js
import { createContext } from "react";
const AppContext = createContext({
isloggedIn: {},
setLoggedIn: () => {},
});
export default AppContext;
Here is my App.js:
import AppContext from "./src/Component/AppContext";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
isloggedIn: false,
};
this.loginStatusCheck();
}
loginStatusCheck = async () => {
const userToken = await AsyncStorage.getItem("#storage_Key");
if (userToken) {
this.setState({ isloggedIn: true });
} else {
this.setState({ isloggedIn: false });
}
};
render() {
return (
<AppContext.Provider
value={{
isloggedIn: this.state.isloggedIn,
setLoggedIn: this.setLoggedIn,
}}
>
<NavigationContainer>
{this.state.isloggedIn ? (
<DrawerNavigator />
) : (
<LoginStackNavigator />
)}
</NavigationContainer>
</AppContext.Provider>
);
}
}
And here is my LoginScreen.js:
import AppContext from "../../Component/AppContext";
const LoginCall = () => {
const { setLoggedIn } = useContext(AppContext);
return setLoggedIn(true);
};
export default class LoginScreen extends Component {
login = async () => {
const { email, password, confirmpassword } = this.state;
console.log(email);
axios
.post("http://127.0.0.1:8002/rest-auth/login/", {
username: "username",
email: "default#email.com",
password: "password",
})
.then((response) => {
console.log(response.data.key);
this.storeKey(response.data.key);
LoginCall;
})
//.then(this.props.navigation.navigate("HomeScreen"))
.catch((error) => {
console.log(error);
});
};
storeKey = async (value) => {
try {
await AsyncStorage.setItem("#storage_Key", value);
} catch (e) {
console.log("error" + e);
} finally {
console.log("done");
}
};
render() {
return (
<View
style={{
backgroundColor: "#fff",
paddingTop: 40,
alignItems: "center",
flex: 1,
}}
>
<TouchableOpacity onPress={() => this.login()}>
<Text>Login</Text>
</TouchableOpacity>
</View>
);
}
}
I think its because you're not importing axios on LoginSreen.
Try to add import axios from 'axios' with others importations
I'm new to react native concepts. I'm trying to pass functions and a parameter to context API so that I can access those in my child component. I'm trying to implement basic login functionality that will show different messages based on user login status. The functionality works when I pass the SignIn method to my child component but the same function is not accessible when I send it along with some variable. The below code and notes can explain the problem clearly.
In the below code as you can see I'm passing my reducer function and initial states from which I get the error messages and sign function <AuthenticationContext.Provider value={[authContext, initialLoginState]}>
App.js
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import BottomNavigation from './src/Navigations/BottomNavigation';
import AuthStackNavigation from './src/Navigations/AuthStackNavigation'
import { useEffect, useMemo, useReducer } from 'react';
import { View } from 'react-native-animatable';
import apiCaller from "./src/api/apiCaller";
import { ActivityIndicator, Text } from 'react-native';
import { AuthenticationContext } from './src/context/AuthenticationContext'
import { Provider as VideoProvider } from './src/context/videoContext'
import { Context as VideoContext } from './src/context/videoContext'
import AsyncStorage from '#react-native-async-storage/async-storage'
export default function App() {
//Initial state values
const initialLoginState = {
isLoading: true,
userName: null,
userToken: null,
errorMessage: ''
}
//Reducer function
const loginReducer = (prevState, action) => {
switch (action.type) {
case 'LOGIN':
return {
...prevState,
userToken: action.token,
userName: action.id,
isLoading: false
};
case 'LOGOUT':
return {
...prevState,
userName: null,
userToken: null,
isLoading: false,
};
case 'REGISTER':
return {
...prevState,
userToken: action.token,
userName: action.id,
isLoading: false,
};
case 'ERROR':
return {
...prevState,
errorMessage: action.response,
isLoading: false
};
default:
return { userToken: null }
}
}
//Defining useReducer
const [newLoginState, dispatch] = useReducer(loginReducer, initialLoginState);
const authContext = useMemo(() => ({
signIn: async (email, password) => {
try {
const userData = {
email: email,
password: password,
};
const response = await apiCaller.post("/login", userData);
if (response.data.code == '200') {
await AsyncStorage.setItem('userToken', response.data.token)
dispatch({ type: 'LOGIN', id: email, token: response.data.token })
}
else if (response.data.code == '404') {
dispatch({ type: 'ERROR', response: response.data.message })
}
} catch (err) {
console.log(err);
}
}
}), []);
return (
<VideoProvider value={VideoContext}>
<AuthenticationContext.Provider value={{authContext, initialLoginState}}>
<NavigationContainer>
{newLoginState.userToken == null ?
<AuthStackNavigation />
:
<BottomNavigation />
}
</NavigationContainer>
</AuthenticationContext.Provider>
</VideoProvider>
);
}
and in the below file, I'm fetching the provider vales by
const { signIn, initialLoginState } = useContext(AuthenticationContext) but its giving me "signIn is not a function. (In 'signIn(email, password)', 'signIn' is undefined)" error but signIn method is accessible when i just try to pass and access SignIn method alone.
Signinscreen.js
import React, { useState } from 'react'
import { Ionicons, MaterialIcons } from '#expo/vector-icons';
import { Text, View, StyleSheet, TouchableOpacity, Platform, StatusBar } from 'react-native';
import { WindowHeight, WindowWidth } from '../utils/PlatformDimention'
import PrimaryFormInput from "../components/PrimaryFormInput";
import PrimaryFormButton from "../components/PrimaryFormButton";
import { AuthenticationContext } from "../context/AuthenticationContext";
import { useContext } from 'react';
const UserLogin = ({ navigation }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { signIn, initialLoginState } = useContext(AuthenticationContext)
const loginHandle = (email, password) => {
signIn(email, password);
}
return (
<View style={styles.container}>
<StatusBar barStyle='light-content' />
<View style={styles.header}>
<View style={{ flex: 1, }}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<MaterialIcons style={styles.goBack} name="arrow-back" size={WindowHeight / 27} />
</TouchableOpacity>
</View>
<View style={{ flex: 2, alignItems: 'center', justifyContent: 'center', }}>
<Ionicons
style={{ color: "#fff" }} name="logo-bitbucket" size={WindowHeight * 10 / 100} />
</View>
<View style={{ flex: 1, alignItems: 'flex-end' }}>
</View>
</View>
<View style={styles.footer}>
<View style={styles.topFlex}></View>
<View style={styles.middleFlex}>
<Text style={styles.loginText}>Welcome!</Text>
<Text style={styles.loginSubTextSub}>Login to your existing account.</Text>
<PrimaryFormInput
inputValue={email}
onChangeText={(userEmail) => setEmail(userEmail)}
inputPlaceHolder='Email'
iconName="user"
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
/>
<PrimaryFormInput
inputValue={password}
onChangeText={(userPassword) => setPassword(userPassword)}
inputPlaceHolder='Password'
iconName="key"
secureTextEntry={true}
/>
<PrimaryFormButton
ButtonText='Login'
onPress={() => loginHandle(email, password)}
/>
</View>
<View style={styles.bottomFlex}></View>
</View>
</View>
)
}
export default UserLogin
Could anyone of you please let me know how to pass both method and parameters to my child component and access it?
Your mistake is just here
const { signIn, initialLoginState } = useContext(AuthenticationContext)
Because you pass the context object like this
<AuthenticationContext.Provider value={{authContext, initialLoginState}}>
You need to do the destructruring like this
const { authContext : { signIn }, initialLoginState } = useContext(AuthenticationContext)
I'm doing wrong with the navigation, I use SwitchNavigator to handle the auth and app navigation
After performing login I try to redirect user to the AppNavigation but Im got this error :
undefined is not an object (evaluating 'this.props.navigation')
Navigation.js
import { createSwitchNavigator, createAppContainer } from 'react-navigation'
import AuthNavigation from './AuthNavigation'
import AppNavigation from './AppNavigation'
const SwitchNavigator = createSwitchNavigator(
{
Auth: AuthNavigation,
App: AppNavigation
},
{
initialRouteName: 'Auth'
}
)
const AppContainer = createAppContainer(SwitchNavigator)
export default AppContainer
LoginScreen.js
import React, { memo, useState } from 'react';
import { TouchableOpacity, StyleSheet, Text, View } from 'react-native';
import Background from '../Components/Background';
import Logo from '../Components/Logo';
import Header from '../Components/Header';
import Button from '../Components/Button';
import TextInput from '../Components/TextInput';
import BackButton from '../Components/BackButton';
import { theme } from '../CustomProperties/Themes';
import { emailValidator, passwordValidator } from '../CustomProperties/utils';
import Login from '../Components/Login';
const LoginScreen = ({ navigation }) => {
const [email, setEmail] = useState({ value: '', error: '' });
const [password, setPassword] = useState({ value: '', error: '' });
const _onLoginPressed = () => {
const emailError = emailValidator(email.value);
const passwordError = passwordValidator(password.value);
if (emailError || passwordError) {
setEmail({ ...email, error: emailError });
setPassword({ ...password, error: passwordError });
return;
}
else {
const login = new Login();
login.realmLogin(email.value, password.value);
}
};
return (
<Background>
<BackButton goBack={() => navigation.navigate('HomeScreen')} />
<Logo />
<Header>Welcome back.</Header>
<TextInput
label="Email"
returnKeyType="next"
value={email.value}
onChangeText={text => setEmail({ value: text, error: '' })}
error={!!email.error}
errorText={email.error}
autoCapitalize="none"
autoCompleteType="email"
textContentType="emailAddress"
keyboardType="email-address"
/>
<TextInput
label="Password"
returnKeyType="done"
value={password.value}
onChangeText={text => setPassword({ value: text, error: '' })}
error={!!password.error}
errorText={password.error}
secureTextEntry
/>
<View style={styles.forgotPassword}>
<TouchableOpacity
onPress={() => navigation.navigate('ForgotPasswordScreen')}
>
<Text style={styles.label}>Forgot your password?</Text>
</TouchableOpacity>
</View>
<Button mode="contained" onPress={_onLoginPressed}>
Login
</Button>
<View style={styles.row}>
<Text style={styles.label}>Don’t have an account? </Text>
<TouchableOpacity onPress={() => navigation.navigate('RegisterScreen')}>
<Text style={styles.link}>Sign up</Text>
</TouchableOpacity>
</View>
</Background>
);
};
const styles = StyleSheet.create({
forgotPassword: {
width: '100%',
alignItems: 'flex-end',
marginBottom: 24,
},
row: {
flexDirection: 'row',
marginTop: 4,
},
link: {
fontWeight: 'bold',
},
});
export default memo(LoginScreen);
Login.js
import React, { Component } from 'react';
import {
Alert,
Button,
KeyboardAvoidingView,
Image,
StyleSheet,
Text,
TouchableHighlight,
View
} from 'react-native';
import Auth0 from 'react-native-auth0';
var credentials = require('../auth0-credentials');
const auth0 = new Auth0(credentials);
export default class Login {
constructor(props) {
//super(props);
this.state = { viewLogin: true };
this.realmLogin = this.realmLogin.bind(this);
this.createUser = this.createUser.bind(this);
}
navigateHomeApp() {
this.props.navigation.navigate("App");
};
onSuccess(credentials) {
auth0.auth
.userInfo({ token: credentials.accessToken })
.then(profile => {
//console.log(credentials);
//console.log(profile);
this.props.onAuth(credentials, profile);
})
.catch(console.error);
}
alert(title, message) {
Alert.alert(
title,
message,
[{ text: 'OK', onPress: () => console.log('OK Pressed') }],
{ cancelable: false }
);
}
realmLogin(username, password) {
auth0.auth
.passwordRealm({
username: username,
password: password,
realm: 'Username-Password-Authentication',
scope: 'openid profile email',
audience: 'https://' + credentials.domain + '/userinfo'
})
.then(credentials => {
this.onSuccess(credentials);
this.navigateHomeApp();
})
.catch(console.error);
}
createUser(username, password) {
auth0.auth
.createUser({
email: username,
password: password,
connection: 'Username-Password-Authentication',
})
.then(success => {
console.log(success)
this.alert('Success', 'New user created')
//navigation.navigate('LoginScreen');
})
.catch(error => {
this.alert('Error', error.json.description)
});
}
webAuth(connection) {
auth0.webAuth
.authorize({
scope: 'openid profile email',
connection: connection,
audience: 'https://' + credentials.domain + '/userinfo'
})
.then(credentials => {
this.onSuccess(credentials);
})
.catch(error => this.alert('Error', error.error_description));
};
}
Sound like I can't access the navigation object who was first declare on the LoginScreen
const LoginScreen = ({ navigation })
Any help would be appreciate
You are calling this.props.navigation.navigate("App") in Login.js class which is just utility class. Navigation works in component
LoginScreen.js
const onNavigateCallback = () => {
this.props.navigation.navigate("App");
}
const _onLoginPressed = () => {
........
if (emailError || passwordError) {
......
}
else {
const login = new Login();
login.realmLogin(email.value, password.value, onNavigateCallback); // Add this here
}
};
Login.js
realmLogin(username, password, callback = undefined) {
auth0.auth
.passwordRealm({
username: username,
password: password,
realm: 'Username-Password-Authentication',
scope: 'openid profile email',
audience: 'https://' + credentials.domain + '/userinfo'
})
.then(credentials => {
this.onSuccess(credentials);
// this.navigateHomeApp(); // remove this from here
if(callback) callback(); // Add this here
})
.catch(console.error);
}
try
navigateHomeApp =()=> {
this.props.navigation.navigate("App");
};
I am new to Rematch for Redux. I am redoing my application using Rematch, but I do not know if I am doing any of this correctly? How/Where do I trigger my reducers and effects (Step 3 in Rematch Docs)? I cannot see my state when I switch between screens. The state seems to be resetting. Could someone please help me? I would greatly appreciate it.
I followed this: https://github.com/rematch/rematch
Here is my code down below:
My application will get bigger, thus the reason why I placed these in different files to build up into the models.js.
store/user.js
const user = {
state: {},
reducers: {
login(payload) { return payload },
email(state, payload) {
return { ...state, payload }
},
password(state, payload) {
return { ...state, payload }
},
username(state, payload) {
return { ...state, payload }
},
fullname(state, payload) {
return { ...state, payload }
},
}
}
export default user
store/index.js
import { init } from '#rematch/core'
import user from './user'
const models = {
user,
}
const store = init({
models,
})
export default store
App.js
import * as React from 'react';
import SwitchNavigator from './navigation/switchNavigator'
import { Provider } from 'react-redux'
import 'redux'
import store from './store/index'
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<SwitchNavigator/>
</Provider>
);
}
}
navigation/switchNavigator
import * as React from 'react';
import TabNavigatorScreen from './tabNavigator'
import AuthNavigatorScreen from './authNavigator'
import { createAppContainer } from 'react-navigation';
import { createSwitchNavigator } from 'react-navigation-switch-transitioner'
const switchNavigator = createSwitchNavigator(
{
Home: {
screen: TabNavigatorScreen
},
Auth: {
screen: AuthNavigatorScreen
}
},
{
initialRouteName: 'Auth',
}
);
export default createAppContainer(switchNavigator);
navigation/authNavigator
import * as React from 'react';
import LoginScreen from '../screens/login'
import SignUpScreen from '../screens/signup'
import MainScreen from '../screens/main'
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
const authNavigator = createStackNavigator(
{
Main: {
screen: MainScreen,
navigationOptions: {
header: null,
}
},
Login: {
screen: LoginScreen,
navigationOptions: {
title: 'Login',
headerTintColor: '#404040',
headerTitleStyle: {
color: '#404040',
},
headerBackTitleStyle: {
color: '#404040',
},
headerBackTitle: null,
}
},
Signup: {
screen: SignUpScreen,
navigationOptions: {
title: 'Sign Up',
headerTintColor: '#404040',
headerTitleStyle: {
color: '#404040',
},
headerBackTitleStyle: {
color: '#404040',
},
headerBackTitle: null,
}
}
},
{
initialRouteName: 'Main',
}
);
export default createAppContainer(authNavigator);
screens/signUp
import * as React from 'react';
import {
TextInput,
Text,
KeyboardAvoidingView,
SafeAreaView,
TouchableOpacity,
Alert,
}
from 'react-native';
import styles from '../styles'
import { connect } from 'react-redux';
import '#expo/vector-icons';
import 'redux';
class Signup extends React.Component {
render() {
const { routeName } = this.props.navigation.state
return (
<SafeAreaView style={styles.container}>
<KeyboardAvoidingView behavior='position'>
<Text style={styles.mainText}>
EMAIL
</Text>
<TextInput
style={styles.inputText}
editable={routeName === 'Signup' ? true : false}
value={this.props.user.email}
onChangeText={input => this.props.email(input)}
/>
<Text style={styles.mainText}>
PASSWORD
</Text>
<TextInput
style={styles.inputText}
editable={routeName === 'Signup' ? true : false}
value={this.props.user.password}
onChangeText={input => this.props.password(input)}
secureTextEntry={true}
/>
<Text style={styles.mainText}>
USERNAME
</Text>
<TextInput
style={styles.inputText}
value={this.props.user.username}
onChangeText={input => this.props.username(input)}
/>
<Text style={styles.mainText}>
FULL NAME
</Text>
<TextInput
style={styles.inputText}
value={this.props.user.fullname}
onChangeText={input => this.props.fullName(input)}
/>
<TouchableOpacity
style={styles.buttonLighGray}
onPress={() => Alert.alert('Sign up')}>
<Text style={styles.buttonDarkText}>
Accept & Sign Up
</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
}
const mapState = (state) => ({
user: state.user,
})
const mapDispatch = ({ user: { email, password, username, fullname }}) => ({
email: () => email(),
password: () => password(),
username: () => username(),
fullname: () => fullname(),
})
export default connect(mapState, mapDispatch)(Signup)
The Login screen is using the same state as Sign up screen.
The state will never change because you don't pass any parameters while dispatching an action.
const mapDispatch = dispatch => ({
setEmail: mail => dispatch.user.email(mail)
})
Everytime you called a function before you just invoked the reducer without a parameter.
Regarding your rematch model. Your reducers should not just return ...state & payload. Try to
const user = {
state: {},
reducers: {
email(state, email) {
return { ...state, email }
},
password(state, password) {
return { ...state, password }
},
username(state, username) {
return { ...state, username }
},
fullname(state, fullname) {
return { ...state, fullname }
},
}
}
I'm fetching data from a web service using graphql my client code is this
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
ActivityIndicator,
View,
FlatList,
TouchableHighlight,
TextInput,
Button,
} from 'react-native';
import PropTypes from 'prop-types';
import { graphql, compose } from 'react-apollo';
import ZONES_QUERY from '../graphql/zones.query'
class ZonesScreen extends Component {
render() {
const { zones, loading, error } = this.props;
if (loading) {
return (
<ActivityIndicator style={styles.activityIndicator} size='large' />
)
} else if (error) {
return (
<View style={styles.container}>
<Text>Unexcpeted Error</Text>
<Button title="retry" onPress={() => { this.props.refetch() }}></Button>
</View>
)
} else {
return (
<View
style={styles.container}>
<FlatList
data={zones}
renderItem={({ item }) => ZoneRenderItem(item)}
keyExtractor={this.keyExtractor}
/>
</View>
)
}
}
//HELPER FUNCTIONS
keyExtractor = item => item._id;
}
ZonesScreen.propTypes = {
refetch: PropTypes.func,
}
const zonesQuery = graphql(ZONES_QUERY, {
options: {
forceFetch: true,
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
},
props: ({ data: { loading, getRoutes, error, refetch } }) => ({
loading,
zones: getRoutes,
refetch,
error,
}),
})
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#eee',
},
activityIndicator: {
flex: 1,
justifyContent: 'center',
},
});
export default compose(
zonesQuery,
)(ZonesScreen)
refetch is not working, when I press retry button it gets the response but component is not being reloaded.
Zones is a list of zones that I get from the service.
As you can see I have set three options to the query
forceFetch: true,
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
all this read from github repository
https://github.com/apollographql/apollo-client/issues/1622
UPDATE!!!!!!!
I found a workaround using Actions from 'react-native-router-flux' basically the trick is force the rendering of component using Actions.refresh an pass props as parameters to reload them. see function refetch = () => { . I also create an refetching state that tells if data is refetching.
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
ActivityIndicator,
View,
FlatList,
TouchableHighlight,
TextInput,
Button,
} from 'react-native';
import PropTypes from 'prop-types';
import { graphql, compose } from 'react-apollo';
import ZONES_QUERY from '../graphql/zones.query
class ZonesScreen extends Component {
constructor(props) {
super(props)
this.state = { refetching: false }
}
render() {
const { zones, loading, error } = this.props;
const { refetching } = this.state
if (loading || refetching) {
return (
<ActivityIndicator style={styles.activityIndicator} size='large' />
)
} else if (error) {
return (
<View style={styles.container}>
<Text>Unexcpeted Error</Text>
<Button title="retry" onPress={() => { this.refetch() }}></Button>
</View>
)
} else {
return (
<View
style={styles.container}>
<FlatList
data={zones}
renderItem={({ item }) => ZoneRenderItem(item)}
keyExtractor={this.keyExtractor}
/>
</View>
)
}
}
//HELPER FUNCTIONS
keyExtractor = item => item._id;
refetch = () => {
this.setState({ refetching: true });
this.props.refetch()
.then(() => { this.refreshComponent(false, this.props)
})
.catch(error => { this.refreshComponent(false, this.props);
})
}
refreshComponent = (refetching, props) => {
this.setState({ refetching: refetching });
Actions.refresh(props)
}
}
const zonesQuery = graphql(ZONES_QUERY, {
options: {
errorPolicy: 'ignore',
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
},
props: ({ data: { loading, getRoutes, error, refetch, } }) => ({
loading,
zones: getRoutes,
error,
refetch,
}),
})
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#eee',
},
activityIndicator: {
flex: 1,
justifyContent: 'center',
},
});
export default compose(
zonesQuery,
)(ZonesScreen)