React-Native Navigation and Promise Rejection - javascript

I have broken down into a simple example something I cannot solve. The code below appears to work fine to the point where I add navigation into the check function, as indicated below.
It then fails with "Possible Unhandled Promise Rejection (id: 0)"
App.js
import React from 'react';
import { Text, View, Button, } from 'react-native';
import { NavigationContainer, useNavigation } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { HomeScreen, NewScreen } from './ui/screens.js';
import { apiCall } from './api/server.js';
const Stack = createStackNavigator();
const App = () => {
const check = async () => {
const navigation = useNavigation(); <--------- Added, then failure
try {
const response = await apiCall();
console.log(response);
navigation.navigate("NewScreen"); <------- Added, then failure
}
catch(err) {
console.log("Caught: " + err);
}
};
return (
<NavigationContainer>{
<Stack.Navigator>
<Stack.Screen name="Home">
{(props) => (
<HomeScreen {...props} check={check} />
)}
</Stack.Screen>
<Stack.Screen name="New"
component={NewScreen}
/>
</Stack.Navigator>
}</NavigationContainer>
);
}
export default App;
server.js
import base64 from 'react-native-base64';
import * as global from './defs.js';
export const apiCall = async () => {
try {
const response = await fetch(global.SITE + "/ttt.php", {
method: 'POST',
headers: {
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Authorization': 'Basic ' + base64.encode(global.BASICAUTHUSER + ':' + global.BASICAUTHPWD)
}
});
console.log(response);
if (!response.ok) {
return new Promise((resolve, reject) => {
reject(new Error(httpError(response.status)));
});
}
return response.json();
}
catch(err) {
return err;
};
};
screens.js
import React from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
export function HomeScreen({ check, navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Next" onPress={check} />
</View>
);
}
export function NewScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Activation Screen.</Text>
</View>
);
}
Any help gratefully received.

you are trying to use useNavigation() hook but your component is not inside
<NavigationContainer>.
i'll suggest you use your navigation/apiCall function inside HomeScreen and from there you can navigate easily.
more ref useNavigation Hook

Related

I have a problem with my provider to render different navigator stacks

I am implementing a provider which helps me to have the state of my user in different views, the main function of this provider is to render different one or the other stack navigator depending on whether the variable is full or empty, this in order to be able to generate two groups of screens depending on whether the user is authenticated or not.
Here my code:
View router.tsx:
import { NavigationContainer } from "#react-navigation/native"
import React, { useContext, useEffect, useState, useRef } from "react"
import { UserContext } from "./context/Usuario"
import AuthStack from "./routes/AuthStack"
import GeneralStack from "./routes/GeneralStack"
const Router = () => {
const { me } = useContext(UserContext)
const auth = useRef(false)
useEffect(() => {
return () => {
auth.current = me !== null
console.log("Hola")
}
}, [me])
return (
<NavigationContainer>
{auth.current ? <GeneralStack /> : <AuthStack />}
</NavigationContainer>
)
}
export default Router
Provider user,js:
import React, { useEffect, createContext, useState } from "react"
import AsyncStorage from "#react-native-async-storage/async-storage"
export const UserContext = createContext()
const UserProvider = ({ children }) => {
const [me, setMe] = useState(undefined)
const validStorage = async () => {
try {
const miSesion = await AsyncStorage.getItem("sesion")
console.log(miSesion)
setMe(JSON.parse(miSesion))
} catch (error) {
console.log(`ERROR: ${error.message}`)
}
}
useEffect(() => {
validStorage()
}, [])
return (
<UserContext.Provider value={{ me, setMe }}>
{children}
</UserContext.Provider>
)
}
export default UserProvider
GeneralStack:
import { createNativeStackNavigator } from "#react-navigation/native-stack"
import React from "react"
import TabStack from "./TabStack"
//import TabStack from "./TabStack"
const GeneralScreen = createNativeStackNavigator()
const GeneralStack = () => {
return (
<GeneralScreen.Navigator screenOptions={{ headerShown: false }}>
<GeneralScreen.Screen name="Tabs" component={TabStack} />
</GeneralScreen.Navigator>
)
}
export default GeneralStack
AuthStack:
import { createNativeStackNavigator } from "#react-navigation/native-stack"
import React from "react"
import Login from "../Screens/Login"
import Registro from "../Screens/Registro/Registro"
import SplashScreen from "../SplashScreen"
const AuthScreen = createNativeStackNavigator()
const AuthStack = () => {
return (
<AuthScreen.Navigator
initialRouteName="Splash"
screenOptions={{ headerShown: false }}>
<AuthScreen.Screen name="Login" component={Login} />
<AuthScreen.Screen name="Register" component={Registro} />
<AuthScreen.Screen name="Splash" component={SplashScreen} />
</AuthScreen.Navigator>
)
}
export default AuthStack
Login:
import React, { useState, useContext } from "react"
import {
Image,
ScrollView,
StatusBar,
Text,
TouchableOpacity,
View,
} from "react-native"
import { useNavigate } from "../../Hooks/useNavigate"
import MyTextInput from "../../components/MyTextInput"
import colors from "../../styles/colors"
import { loginStyles } from "../../styles/styles"
import { UserContext } from "../../context/Usuario"
import AsyncStorage from "#react-native-async-storage/async-storage"
export default function Login() {
const [, setIsSession] = useState(false)
const { setMe } = useContext(UserContext)
const navigate = useNavigate()
const [hidePassword, sethidePassword] = React.useState(false)
const [user] = useState({ user: "admin", password: "admin123" })
const [form, setForm] = useState({ user: "", password: "" })
const getStorage = async () => {
if (await AsyncStorage.getItem("sesion")) {
setIsSession(true)
} else {
setIsSession(false)
}
}
const signIn = async () => {
try {
console.log(user)
if (form.user === user.user && form.password === user.password) {
await AsyncStorage.setItem("sesion", JSON.stringify(form))
setMe(form)
setIsSession(true)
}
} catch (error) {
console.error(error)
}
}
const closeSesion = async () => {
await AsyncStorage.removeItem("sesion")
getStorage()
}
return (
<ScrollView contentContainerStyle={[loginStyles.container]}>
<StatusBar backgroundColor={colors.PURPLE} translucent={true} />
<View style={loginStyles.logo}>
<Image
source={require("../../recursos/images/Logo.png")}
style={{ height: 250, width: 250 }}
/>
</View>
<MyTextInput
onChangeText={(text: string) => {
setForm(state => ({ ...state, user: text }))
}}
keyboardType="email-address"
placeholder="E-mail"
/>
<MyTextInput
onChangeText={(text: string) => {
setForm(state => ({ ...state, password: text }))
}}
keyboardType={null}
placeholder="Contraseña"
bolGone={true}
secureTextEntry={hidePassword}
onPress={() => sethidePassword(!hidePassword)}
/>
<View style={loginStyles.btnMain}>
<TouchableOpacity onPress={signIn}>
<Text style={loginStyles.btntxt}>Iniciar Sesión</Text>
</TouchableOpacity>
</View>
<View style={loginStyles.btnTransparent}>
<TouchableOpacity
onPress={() => navigate({ screen: "Register" })}>
<Text
style={[loginStyles.btntxt, { color: colors.PURPLE }]}>
Registrarse
</Text>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity>
<Text
style={[
loginStyles.txtTransparent,
{ textDecorationLine: "none" },
]}>
Olvide mi contraseña
</Text>
</TouchableOpacity>
</View>
</ScrollView>
)
}
Home:
import AsyncStorage from "#react-native-async-storage/async-storage"
import React, { useState } from "react"
import { Image, ScrollView, TouchableOpacity } from "react-native"
import { Calendar } from "react-native-calendars"
import { Text } from "react-native-elements"
import { useNavigate } from "../../Hooks/useNavigate"
import colors from "../../styles/colors"
const Home = () => {
const [showModal, setShowModal] = useState(false)
const [date, setDate] = useState<string>()
const navigate = useNavigate()
const [isSession, setIsSession] = useState(false)
const getStorage = async () => {
const data = await AsyncStorage.getItem("sesion")
console.log(data)
if (data) {
setIsSession(true)
} else {
setIsSession(false)
}
}
const closeSesion = async () => {
await AsyncStorage.removeItem("sesion")
getStorage()
}
return (
<ScrollView>
<TouchableOpacity onPress={() => closeSesion()}>
<Text>Hola porque soy bien molon</Text>
</TouchableOpacity>
<Image
source={require("../../recursos/images/coralio_logo.png")}
style={{
marginTop: 40,
height: 110,
width: "80%",
justifyContent: "center",
alignSelf: "center",
}}
/>
<Calendar
theme={{
selectedDayBackgroundColor: colors.BLACK,
arrowColor: colors.WHITE,
monthTextColor: colors.WHITE,
}}
style={{
backgroundColor: colors.PURPLE,
borderRadius: 10,
elevation: 4,
marginTop: 60,
margin: 10,
height: 400,
}}
onDayPress={day => {
console.log(day.dateString)
setDate(day.dateString)
setShowModal(false)
}}
onMonthChange={() => {}}
initialDate={"2023-01-16"}
minDate={new Date()
.toLocaleDateString("es-US", {
year: "numeric",
month: "2-digit",
day: "numeric",
formatMatcher: "basic",
})
.split("/")
.reverse()
.join("-")}
markedDates={{
day: {
marked: true,
dotColor: colors.WHITE,
selected: true,
selectedColor: colors.PURPLE,
},
}}
//maxDate={"2023-12-31"}
//hideExtraDays={false}
//disableArrowLeft={true}
//disableArrowRight={true}
//hideArrows={true}
//hideDayNames={true}
/>
</ScrollView>
)
}
export default Home
The problem I have is when doing the close session in home, if in login it sends me from the authstack view to generalstack, when I do the close session it doesn't send me back to login, but it does clean the state of the variable from asyncstorage.
Help :(
It looks like you're not clearing the me variable in your context when the user ends the session. I think your closeSesion method should look like this:
const closeSesion = async () => {
setMe(null)
await AsyncStorage.removeItem("sesion")
getStorage()
}

Button that leads to another screen/page in react native

i'am fairly new to react native and i'm doing a social media clone. I've got my navigator and the login via a database done , but i was wondering if there was a way to link a page/screen to another one by pressing a button .
Here is my code for the home screen after logging in :
import React, {Component} from 'react';
import {View, Text, FlatList} from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
class HomeScreen extends Component {
constructor(props){
super(props);
this.state = {
isLoading: true,
listData: []
}
}
componentDidMount() {
this.unsubscribe = this.props.navigation.addListener('focus', () => {
this.checkLoggedIn();
});
this.getData();
}
componentWillUnmount() {
this.unsubscribe();
}
getData = async () => {
const value = await AsyncStorage.getItem('#session_token');
return fetch("http://localhost:3333/api/1.0.0/search", {
'headers': {
'X-Authorization': value
}
})
.then((response) => {
if(response.status === 200){
return response.json()
}else if(response.status === 401){
this.props.navigation.navigate("Login");
}else{
throw 'Something went wrong';
}
})
.then((responseJson) => {
this.setState({
isLoading: false,
listData: responseJson
})
})
.catch((error) => {
console.log(error);
})
}
checkLoggedIn = async () => {
const value = await AsyncStorage.getItem('#session_token');
if (value == null) {
this.props.navigation.navigate('Login');
}
};
render() {
if (this.state.isLoading){
return (
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text>Loading..</Text>
</View>
);
}else{
return (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<Text>Welcome to The app </Text>
</View>
);
}
}
}
export default HomeScreen;
Now ideally i would want a button in my else statement which could lead me to another screen (eg main screen of the app ) after logging in .
App.js :
import 'react-native-gesture-handler';
import React, { Component } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import HomeScreen from './screens/home';
import LoginScreen from './screens/login';
import SignupScreen from './screens/signup';
import LogoutScreen from './screens/logout';
const Drawer = createDrawerNavigator();
class App extends Component{
render(){
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Login" component={LoginScreen} />
<Drawer.Screen name="Signup" component={SignupScreen} />
<Drawer.Screen name="Logout" component={LogoutScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
}
}
export default App;
If you're new to react native, I'd suggest that you give functional components and hooks a try, this is how your code would look like as a functional component after some clean up
(The answer is included in component)
import React, { Component, useEffect, useState } from "react";
import { View, Text, FlatList, Button } from "react-native";
import AsyncStorage from "#react-native-async-storage/async-storage";
const HomeScreen = (props) => {
const [isLoading, setIsLoading] = useState(true);
const [listData, setListData] = useState([]);
useEffect(() => {
const subscription = props.navigation.addListener("focus", () => {
checkLoggedIn();
});
getData();
return subscription.remove(); //similar to component unmount, syntax might be different based on your version
}, []);
const getData = async () => {
setIsLoading(true)
const value = await AsyncStorage.getItem("#session_token");
return fetch("http://localhost:3333/api/1.0.0/search", {
headers: {
"X-Authorization": value,
},
})
.then((response) => {
if (response.status === 200) {
return response.json();
} else if (response.status === 401) {
props.navigation.navigate("Login");
} else {
throw "Something went wrong";
}
})
.then((responseJson) => {
setIsLoading(false);
setListData(responseJson);
})
.catch((error) => {
console.log(error);
});
};
const checkLoggedIn = async () => {
const value = await AsyncStorage.getItem("#session_token");
if (value == null) {
props.navigation.navigate("Login");
}
};
return (
<>
{isLoading ? (
<View
style={{
flex: 1,
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<Text>Loading..</Text>
</View>
) : (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>Welcome to The app </Text>
<Button
title="Next Page"
onPress={() => {
props.navigation.navigate("yourScreenName");
}}
/>
</View>
)}
</>
);
};
export default HomeScreen;
since you screens are
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Login" component={LoginScreen} />
<Drawer.Screen name="Signup" component={SignupScreen} />
<Drawer.Screen name="Logout" component={LogoutScreen} />
//you can add more screens here just make sure to import them first
//example
<Drawer.Screen name="Example" component={ExampleScreen} />
// name will be the name you use to navigate to that screen
// component should have the screen you imported
then you should pass one of those names to props.navigation.navigate() so the button would be
<Button
title="Next Page"
onPress={() => {
props.navigation.navigate("Login");
}}
/>
If you want to stick with a class component, then :
import {TouchableOpacity} from "react-native"
In the else part,
<TouchableOpacity style ={{width:100, height:100}} onPress={()=>{this.props.navigation.navigate("screenname")}}></TouchableOpacity>
TouchableOpacity is a blank space on the screen, which can be styled easily, and you have an onPress event. Basically it's like a better version of a button. You can also place other components inside the TouchableOpacity, and make the whole thing clickable

Too many re-renders on search API

I am new to this so I hope this is the right place to get help!
As titled, executing this code is giving me the "Too many re-renders" error on React.
I have tried going through all lines and checking my hooks repeatedly, but nothing seems to work.
I am guessing this is happening due to useEffect, so pasting the code for the relevant components below:
UseResults:
import { useEffect, useState } from 'react';
import yelp from '../api/yelp';
export default () => {
const [results, setResults] = useState([]);
const [errorMessage, setErrorMessage] = useState('');
const searchApi = async () => {
try {
const response = await yelp.get('/search', {
params: {
limit: 50,
term,
location: 'san francisco'
}
});
setResults(response.data.businesses);
} catch (err) {
setErrorMessage('Something went wrong')
}
};
useEffect(() => {
searchApi('pasta');
}, []);
return [searchApi, results, errorMessage];
}
SearchScreen:
import React, { useState } from 'react';
import { Text, StyleSheet } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import ResultsList from '../components/ResultsList';
import SearchBar from '../components/SearchBar';
import useResults from '../hooks/useResults';
const SearchScreen = (navigation) => {
const [term, setTerm] = useState('');
const [searchApi, results, errorMessage] = useResults();
const filterResultsByPrice = (price) => {
return results.filter(result => {
return result.price === price;
});
};
return <>
<SearchBar
term={term}
onTermChange={setTerm}
onTermSubmit={searchApi()}
/>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text>We have found {results.length} results</Text>
<ScrollView>
<ResultsList
results={filterResultsByPrice('$')}
title="Cost Effective"
navigation={navigation}
/>
<ResultsList
results={filterResultsByPrice('$$')}
title="Bit Pricier"
navigation={navigation}
/>
<ResultsList
results={filterResultsByPrice('$$$')}
title="Big Spender"
navigation={navigation}
/>
</ScrollView>
</>
};
const styles = StyleSheet.create({});
export default SearchScreen;
ResultsList:
import React from 'react';
import { View, Text, StyleSheet, FlatList } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import ResultsDetail from './ResultsDetail';
const ResultsList = ({ title, results, navigation }) => {
return (
<View style={styles.container} >
<Text style={styles.title}>{title}</Text>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={results}
keyExtractor={result => result.id}
renderItem={({ item }) => {
return (
<TouchableOpacity onPress={() => navigation.navigate('ResultsShow')}>
<ResultsDetail result={item} />
</TouchableOpacity>
)
}}
/>
</View>
);
};
const styles = StyleSheet.create({
title: {
fontSize: 18,
fontWeight: 'bold',
marginLeft: 15,
marginBottom: 5
},
container: {
marginBottom: 10
}
});
export default ResultsList;
TIA!

On home i get that error: The action 'SET_PARAMS' with the payload {"params":{"company":"Marciello Resto 2"}} was not handled by any navigator

I am now using React native navigation 5 and in my "Home component I have a setParams which works but I get that error:
"The action 'SET_PARAMS' with the payload {"params":{"company":"Marciello Resto 2"}} was not handled by any navigator."
I am on Android Emulator.
Thank you all for your help!
//App.js
import * as React from "react"
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
import { createStackNavigator } from '#react-navigation/stack'
import { createDrawerNavigator } from '#react-navigation/drawer'
import EtiquettesPictureContext from './Components/Contexts/EtiquettesPictureContext'
import RootStack from './Navigation/Navigation'
import Home from './Components/Layouts/Home'
import Etiquettes from './Components/Layouts/Etiquettes'
import TemperaturesFridges from './Components/Layouts/TemperaturesFridges'
import BonsLivraisons from './Components/Layouts/BonsLivraisons'
import EtiquettesTakePicture from "./Components/EtiquettesTakePicture"
import BonsLivraisonsTakePicture from "./Components/BonsLivraisonsTakePicture"
import DlcAlertTakePicture from "./Components/DlcAlertTakePicture"
import Notifications from "./Components/Layouts/Notifications"
import Settings from "./Components/Layouts/Settings"
import Welcome from "./Components/Layouts/Welcome"
import Login from "./Components/Layouts/Login"
import Signup from "./Components/Layouts/Signup"
import InitLogin from "./Components/Layouts/InitLogin"
import MesEtiquettes from "./Components/Layouts/MesEtiquettes"
import WhatRole from "./Components/WhatRole"
const AuthContext = React.createContext()
const Stack = createStackNavigator()
const Drawer = createDrawerNavigator()
export default function App({ navigation }) {
const [etiquettesPictureData, blPictureData, setEtiquettesPictureData, setBlPictureData] = React.useState("")
updateInitialData = (data) => {
setEtiquettesPictureData(data)
}
updateBlData = (data) => {
blPictureData(data)
}
const [state, dispatch] = React.useReducer(
(prevState, action) => {
switch (action.type) {
case 'RESTORE_TOKEN':
return {
...prevState,
userToken: action.isLogged,
isLoading: false,
};
case 'SIGN_IN':
return {
...prevState,
isSignout: false,
userToken: action.isLogged,
};
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 isLogged;
try {
isLogged = await InitLogin.initLogin()
} 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', isLogged: isLogged.logged });
};
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 AsyncStorage
// In the example, we'll use a dummy token
dispatch({ type: 'SIGN_IN', isLogged: 'dummy-auth-token' });
},
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 `AsyncStorage`
// In the example, we'll use a dummy token
dispatch({ type: 'SIGN_IN', isLogged: 'dummy-auth-token' });
},
}),
[]
)
if (state.isLoading) {
// We haven't finished checking for the token yet
return (
<View style={[styles.container, styles.horizontal]}>
)
}
function RootStackScreen() {
return (
<Stack.Navigator>
{state.userToken == false ? (
<>
<Stack.Screen name="Welcome" component={Welcome} />
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Signup" component={Signup} />
</>
) : (
<>
<Stack.Screen name="Home" component={Home} options={({ route }) => ({ title: route.params ? route.params.company : "" })} />
<Stack.Screen name="Etiquettes" component={Etiquettes} />
<Stack.Screen name="TemperaturesFridges" component={TemperaturesFridges} />
</>
)}
</Stack.Navigator>
)
}
function DrawerScreen() {
return (
<Drawer.Navigator>
<Drawer.Screen name="TemperaturesFridges" component={TemperaturesFridges} />
</Drawer.Navigator>
)
}
return (
<EtiquettesPictureContext.Provider
value={{
etiquette: etiquettesPictureData,
Bl: blPictureData,
updateInitialData: updateInitialData,
updateBlData: updateBlData
}}>
<AuthContext.Provider value={authContext}>
<Drawer.Navigator>
<Drawer.Screen name="Home" component={RootStackScreen} />
</Drawer.Navigator>
</AuthContext.Provider>
</EtiquettesPictureContext.Provider>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
},
horizontal: {
flexDirection: 'row',
justifyContent: 'space-around',
padding: 10
}
})
//Home.jsimport * as React from 'react';
import mainStyleSheet from "../../Themes/main.js"
import { MaterialHeaderButtons, Item} from '../HeaderButtons';
import Ionicons from 'react-native-vector-icons/FontAwesome5';
import Company from '../Entities/Company';
import Notification from '../Entities/Notification'
import {
SafeAreaView,
ScrollView,
View,
Text,
TouchableHighlight
} from 'react-native';
export default function Home({ navigation}) {
React.useEffect(() => {
Company.getCompany().then(company => {
navigation.setParams({ company })
})
this.didBlurSubscription = navigation.addListener(
'didFocus',
() => {
Notification.countNotifications()
.then(resp => {
navigation.setParams({ notificationsCount: resp.length })
})
}
)
// returned function will be called on component unmount
return () => {
this.didBlurSubscription()
}
}, [])
const { navigate} = navigation;
return (
<TouchableHighlight style={[mainStyleSheet.buttonHomeContainer, { marginTop: 0 }]} onPress={() => navigate('Etiquettes')} >
<Ionicons name="tag" style={{ marginLeft: 20 }} color="white" size={30} />
Etiquettes produits
<TouchableHighlight style={mainStyleSheet.buttonHomeContainer} onPress={() => navigate('TemperaturesFridges')}>
<Ionicons name="thermometer-quarter" style={{ marginLeft: 20 }} color="white" size={30} />
Relevés de température
<TouchableHighlight style={mainStyleSheet.buttonHomeContainer} onPress={() => navigate('BonsLivraisons')}>
<Ionicons name="file-invoice" style={{ marginLeft: 20 }} color="white" size={30} />
Bons de livraisons
<TouchableHighlight style={mainStyleSheet.buttonHomeContainer} onPress={() => navigate('DownloadableDocuments')}>
<Ionicons name="file-image" style={{ marginLeft: 20 }} color="white" size={30} />
Documents à télécharger
)
}
I believe this issue has to do with calling navigation.setParams when the component is unmounting. According to the react navigation docs [0],
The setParams method lets us update the params (route.params) of the current screen
If the current screen is already unmounting when setParams gets called, then it would make sense that the navigator would not be able to handle it. Rework your logic so you are not setting params on blur.
[0] https://reactnavigation.org/docs/navigation-prop/#setparams

React Native NetInfo 'connectionChange' event not fired

I have two PoCs I'm working on. 1)RNRestAPI. 2)RNNavigate.
In RNRestAPI index.android.js and index.ios.js both looks like this;
import React, { Component } from 'react';
import {
AppRegistry,
View
} from 'react-native';
import Login from './app/screens/Login/Login';
import About from './app/screens/About/About';
export default class RNRestAPI extends Component {
render() {
return (
<View style={{flex:1}}>
<Login />
</View>
);
}
}
AppRegistry.registerComponent('RNRestAPI', () => RNRestAPI);
Login.js is like this;
import React, { Component } from 'react';
import {
AppRegistry,
View,
TextInput,
StyleSheet,
Button,
Text,
Alert,
TouchableHighlight,
Platform,
Image,
NetInfo,
ProgressBarAndroid,
ProgressViewIOS
} from 'react-native';
import I18n from '../../resources/strings/i18n';
import Colors from '../../resources/styles/colors';
import Dimensions from '../../resources/styles/dimensions';
import Styles from '../../resources/styles/styles';
import Config from '../../config';
export default class Login extends Component {
constructor() {
super();
this.state = {
username:'',
password:'',
buttonLoginDisabled:false,
isConnected:false
}
// I18n.locale = 'en';
}
componentWillMount() {
NetInfo.addEventListener(
'connectionChange',
this.handleConnectivityChange.bind(this)
);
}
componentWillUnmount() {
NetInfo.removeEventListener('connectionChange', handleConnectivityChange);
}
handleConnectivityChange(connectionInfo) {
console.log('First change, type: ' + connectionInfo.type + ', effectiveType: ' + connectionInfo.effectiveType);
if(connectionInfo.type === 'none') {
this.setState({
isConnected:false,
buttonLoginDisabled:true
});
} else {
this.setState({
isConnected:true,
buttonLoginDisabled:false
});
}
}
onLoginClicked() {
var valid = this.validateLoginForm();
if(valid === true) {
this.setState({
buttonLoginDisabled:true
});
this.makeLoginRequest();
} else {
Alert.alert(I18n.t('dialogLoginInvalidTitle'), I18n.t('dialogLoginInvalidMessage'));
}
}
validateLoginForm() {
if(this.state.username === '') {
return false;
}
if(this.state.password === '') {
return false;
}
return true;
}
makeLoginRequest() {
fetch('http://webapitest', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'MobilePlatformId': Config.MobilePlatformId,
'ApplicationId': Config.ApplicationId,
'Version': '1.9.6'
},
body: JSON.stringify({
Username: this.state.username,
Password: this.state.password
})
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
if(responseJson.Token !== null) {
console.log('login successful');
} else {
this.setState({
buttonLoginDisabled:false
});
Alert.alert(I18n.t('dialogInvalidLoginTitle'), I18n.t('dialogInvalidLoginMesage'));
}
})
.catch((error) => {
console.log(eror);
this.setState({
buttonLoginDisabled:false
});
})
}
setUsername(value) {
this.setState({
username:value
});
}
setPassword(value) {
this.setState({
password:value
});
}
onMoreClicked() {
Alert.alert(I18n.t('dialogLearnMoreTitle'), I18n.t('dialogLearnMoreMesage'));
}
getLoginButtonStyle() {
if(this.state.buttonLoginDisabled) {
return styles.buttonLoginDisabled;
} else {
return styles.buttonLogin;
}
}
render() {
return (
<View style={styles.container}>
<View>
<Image source={require('../../resources/images/facilit_logo_welcome.png')}
style={{width:266, height:50, resizeMode:Image.resizeMode.cover}} />
</View>
<View style={styles.wrapperLoginInput}>
<TextInput
keyboardType='default'
placeholder={I18n.t('username')}
returnKeyType='next'
onChangeText={(value) => this.setUsername(value)}
style={Styles.primaryTextInput} />
<TextInput secureTextEntry={true}
placeholder={I18n.t('password')}
onChangeText={(value) => this.setPassword(value)}
style={Styles.primaryTextInput} />
<TouchableHighlight onPress={this.onLoginClicked.bind(this)}
style={{marginTop:(Platform.OS === 'ios') ? 10 : 30}}
underlayColor='#00000000'
disabled={this.state.buttonLoginDisabled}>
<View style={this.getLoginButtonStyle()}>
<Text style={styles.buttonLoginText}>{I18n.t('login')}</Text>
</View>
</TouchableHighlight>
<View style={styles.wrapperLearnMoreLink}>
<TouchableHighlight onPress={this.onMoreClicked.bind(this)}
underlayColor='#00000000'>
<Text style={styles.learnMoreLink}>{I18n.t('learnMore')}</Text>
</TouchableHighlight>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex:1,
backgroundColor:Colors.primaryBlue,
justifyContent:'center',
alignItems:'center'
},
wrapperLoginInput: {
width:300,
marginTop:100
},
buttonLogin: {
backgroundColor:Colors.primaryYellow,
alignItems:'center',
height:Dimensions.primaryButtonHeight,
justifyContent:'center',
borderRadius:Dimensions.primaryButtonBorderRadius,
borderWidth:Dimensions.primaryButtonBorderWidth,
borderColor:Colors.primaryButtonBorderColor,
},
buttonLoginDisabled: {
backgroundColor:Colors.primaryButtonDisabledGray,
alignItems:'center',
height:Dimensions.primaryButtonHeight,
justifyContent:'center',
borderRadius:Dimensions.primaryButtonBorderRadius,
borderWidth:Dimensions.primaryButtonBorderWidth,
borderColor:Dimensions.primaryButtonBorderColor,
},
buttonLoginText: {
fontSize:Dimensions.primaryButtonFontSize,
color:Colors.primaryButtonFontColor
},
wrapperLearnMoreLink: {
alignItems:'center',
marginTop:150,
},
learnMoreLink: {
color:Colors.secondaryTextColor,
textDecorationLine:'underline'
}
});
AppRegistry.registerComponent('Login', () => Login);
The important bits are componentWillMount() and handleConnectivityChange(connectionInfo). They work as expected and my code handles online/offline scenarios.
The second PoC(RNNavigate) is basically a copy of RNRestAPI but with the inclusion of react-navigation https://reactnavigation.org/. I'm basically trying to create the navigation for my app after the user logs in successfully into my app. So accordingly I have done the following modification to my code.
1) Create App.js
import React, { Component } from 'react';
import {
AppRegistry,
View
} from 'react-native';
import Login from './app/screens/Login/Login';
import About from './app/screens/About/About';
import FacilitySearch from './app/screens/FacilitySearch/FacilitySearch';
import { StackNavigator } from 'react-navigation';
export default class RNNavigate extends Component {
render() {
return (
<View style={{flex : 1}}>
<RNNavigateApp />
</View>
);
}
}
const RNNavigateApp = StackNavigator({
Login : {
screen : Login,
navigationOptions : ({navigation}) => ({
header : null
})
},
About : { screen : About },
FacilitySearch : {
screen : FacilitySearch,
navigationOptions : ({
headerLeft : null
})
}
});
AppRegistry.registerComponent('RNNavigate', () => RNNavigate);
2) Modify index.android.js and index.ios.js to;
import './App.js';
Login.js is untouched. But the connectionChange event is no longer fired. Any expert help is much appreciated to guide me figuring out as to why it's no longer fired or educate me on if I have done something wrong in terms of using React Navigate.
For me, the event was not fired because I didn't restart my react-native server after adding <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> in the AndroidManifest.xml.
Kill your server and restart it after then it should fire.

Categories

Resources