react native, how to jump page in network request - react-native

I want to jump to the login page when the server returns a 401 status code,Where should i set
my app.js:
...
import React, { Component } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import Test from './pages/test'
import Login from './pages/login'
...
const Stack = createStackNavigator();
class App extends Component{
render(){
return(
<NavigationContainer>
<Stack.Navigator initialRouteName="Test" headerMode="none">
<Stack.Screen name="Test" component={Test} />
<Stack.Screen name="Login" component={Login} />
...
</Stack.Navigator>
</NavigationContainer>
)
}
}
export default App ;
I have a request.js to handle the all request
every page will use this axios instance to send a request
For example
/pages/test
import {test} from './request.js'
import React, {Component} from 'react';
export default class Test extends Component {
componentDidMount(){
test()
}
}
request.js
import axios from 'axios';
const instance = axios.create({
baseURL: 'http://192.168.10.10:51000',
});
instance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if(error.response.status === 401){
/*
*** jump page
*/
}
return Promise.reject(error);
},
);
export function test() {
return instance.get('/test');
}

first,Create a new file named RootNavigation.js:
import React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
and then,Import in app.js,Then add ref={navigationRef} in the NavigationContainer of app.js
import { navigationRef } from './RootNavigation';
// ...
class App extends Component{
render(){
return(
<NavigationContainer ref={navigationRef}>
</NavigationContainer>
)
}
}
Finally, modify request.js like this
import * as RootNavigation from './RootNavigation';
// ...
if(error.response.status === 401){
RootNavigation.navigate('Login');
}
done,It works

Related

Application not loaded after upgrade from React-navigation 4 to React-Navigation 6

after upgrade from React-navigation 4 to React-navigation 6 the default imports cause undefined errors when used for assignments.
The Application uses expo, Expo SDK version is 44.
This error occurs when opening the application
Application error
RoutesNavigators.js
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import ConfigsLoading from './screens/commons/configs_loading';
const Stack = createNativeStackNavigator();
const RoutesNavigator = () => (
<NavigationContainer>
<Stack.Navigator initialRouteName="ConfigsLoading" screenOptions={{ headerShown: false }}>
<Stack.Screen name="ConfigsLoading" component={ConfigsLoading} />
</Stack.Navigator>
</NavigationContainer>
);
export default RoutesNavigator;
configs_loading / index.jsx
/* eslint-disable no-constant-condition */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { View, ActivityIndicator, Text } from 'react-native';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import lojas from '../../features/lojas';
import { styles } from './styles';
import pushTokens from '../../../infra/push_tokens';
import usuarios from '../../features/usuario';
import campanhas from '../../features/campanhas';
class ConfigsLoading extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<View style={styles.container}>
<ActivityIndicator size="large" color="#999999" />
<Text>{this.props.t('commons:carregando')}</Text>
</View>
);
}
}
ConfigsLoading.propTypes = {
navigation: PropTypes.object.isRequired,
credenciais: PropTypes.object.isRequired,
recuperarLojaFavorita: PropTypes.func.isRequired,
setarLojaCorrente: PropTypes.func.isRequired,
recuperarCredencial: PropTypes.func.isRequired,
aceitarTermosUso: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
criarTokenUsuario: PropTypes.func.isRequired,
pushToken: PropTypes.string,
recuperarCampanhaVigente: PropTypes.func.isRequired,
};
ConfigsLoading.defaultProps = {
pushToken: undefined,
};
const mapStateToProps = state => ({
salvandoLojaFavorita: lojas.selectors.obterSalvandoLojaFavorita(state),
pushToken: pushTokens.selectors.obterToken(state),
credenciais: usuarios.selectors.obterCredencial(state),
aceitandoTermosUso: usuarios.selectors.obterAceitandoTermosUso(state),
excluidoCredencialUsuario: usuarios.selectors.obterExcluindoCredencial(state),
});
const obj = translate(['common'], { wait: true })(ConfigsLoading);
export default connect(mapStateToProps, {
...lojas.actions, ...pushTokens.actions, ...usuarios.actions, ...campanhas.actions,
})(obj);
lojas / index.js
import Container from './components/Container';
import Lista from './components/Lista';
import * as constants from './constants';
import * as actions from './actions';
import * as selectors from './selectors';
import reducer from './reducer';
export default {
Container,
Lista,
reducer,
constants,
actions,
selectors,
};
pushTokens, campanhas and usuarios files has te same export as the lojas file
i have the following plugins in my babel.config.js
"babel-plugin-module-resolver",
"react-native-reanimated/plugin"

App.js Navigation error : Couldn't find a navigation object. Is your component inside a screen in a navigator?

I want to redirect my current app screen to login screen if the user is not authenticated. So I created a global component RedirectorToLogin and use this component in my App.js . But I'm getting this error Couldn't find a navigation object. Is your component inside a screen in a navigator ? because I'm using useNavigation inside RedirectorToLogin.
What is the reason for this error to be occured ?
My RedirectorToLogin.js
import React, { useContext, useEffect } from 'react'
import { useNavigation } from '#react-navigation/native'
import AuthGlobal from '../Context/store/AuthGlobal'
const RedirectorToLogin = (props) => {
const context = useContext(AuthGlobal)
const navigation = useNavigation()
useEffect(() => {
if (!context.stateUser.isAuthenticated) {
navigation.navigate('Login')
}
return () => {}
}, [context.stateUser.isAuthenticated])
return <></>
}
export default RedirectorToLogin
My App.js
import { StatusBar } from 'expo-status-bar'
import React from 'react'
import { LogBox } from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
import Toast from 'react-native-toast-message'
import ErrorBoundary from 'react-native-error-boundary'
// Redux
import { Provider } from 'react-redux'
import store from './Redux/store'
// Context API
import Auth from './Context/store/Auth'
// Navigatiors
import Main from './Navigators/Main'
// Screens
import Header from './Shared/Header'
import MyAppState from './Global/MyAppState'
import Network from './Global/Network'
import RedirectorToLogin from './Global/RedirectorToLogin'
const errorHandler = (error, stackTrace) => {
/* Log the error to an error reporting service */
console.log('**** error log form error handler ****')
console.log(error)
console.log(stackTrace)
console.log('**** **** ****')
}
LogBox.ignoreAllLogs(true)
export default function App() {
return (
<ErrorBoundary onError={errorHandler}>
<Auth>
<Provider store={store}>
<NavigationContainer>
<Header />
<Main />
<MyAppState />
<Network />
<RedirectorToLogin />
<Toast ref={(ref) => Toast.setRef(ref)} />
</NavigationContainer>
</Provider>
</Auth>
</ErrorBoundary>
)
}
I referred to this article and updated my Main.js to show only login navigator when user is not authenticated. So I can get rid of having RedirectorToLogin

react native memory leak react navigation

I want to check If the user has a secure Token in a useEffect but I get this error Message.
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application.
This happens when I use the useEffect. If I remove it, then I get no error message but I need to check if the user has the token.
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import getSecureKey from '../utilies/getSecureKey';
const Stack = createStackNavigator();
const AppStack = ({ navigation }) => {
useEffect(() => {
getSecureKey().then(res => console.log(res)).catch(e => console.log(e));
}, []);
return (
<Stack.Navigator showIcon={true} initialRouteName="AppTabs">
<Stack.Screen name="AppTabs" component={AppTabs} options={{headerTitle: () => <Header />, headerStyle: {
backgroundColor: '#fff'
}}} />
.....
getSecureToken:
import * as SecureStore from 'expo-secure-store';
const getSecureKey = async () => {
const key = await SecureStore.getItemAsync('jwt');
return key;
};
export default getSecureKey;
App.js
import React, { useState, useEffect } from 'react';
import * as Font from 'expo-font';
import { NavigationContainer } from '#react-navigation/native';
import AppLoading from 'expo-app-loading';
import { Provider } from 'react-redux';
import store from './src/redux/store/index';
import AppStack from './src/navigation/stack';
const getFonts = async () => {
await Font.loadAsync({
"nunito-regular": require("./assets/fonts/Nunito-Regular.ttf"),
"nunito-bold": require("./assets/fonts/Nunito-Bold.ttf"),
});
};
const App = () => {
const [fontsLoaded, setFontsLoaded] = useState(false);
if(fontsLoaded) {
return (
<Provider store={store}>
<NavigationContainer><AppStack /></NavigationContainer>
</Provider>)
} else {
return (<AppLoading startAsync={getFonts} onFinish={() => setFontsLoaded(true)} onError={() => {}} />)
}
};
export default App;
Don't restore token in the navigator. Instead do this -
Firstly, install expo-app-loading from here
Then, create a folder called navigation where your App.js is located. Then inside it create a File called AppNavigator.js.
Inside AppNavigator.js, paste this
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import { createStackNavigator } from '#react-navigation/stack';
import getSecureKey from '../utilities/getSecureKey';
const Stack = createStackNavigator();
const AppNavigator = () => {
// Remove these Lines --
// useEffect(() => {
// getSecureKey()
// .then((res) => console.log(res))
// .catch((e) => console.log(e));
// }, []);
return (
<Stack.Navigator showIcon={true} initialRouteName="AppTabs">
<Stack.Screen
name="AppTabs"
component={AppTabs}
options={{
headerTitle: () => <Header />,
headerStyle: {
backgroundColor: '#fff',
},
}}
/>
</Stack.Navigator>
);
};
export default AppNavigator;
For your fonts create a folder called hooks where your App.js is located and inside that create a file useFonts.js
In useFonts.js write like this -
import * as Font from "expo-font";
export default useFonts = async () => {
await Font.loadAsync({
"nunito-regular": require("./assets/fonts/Nunito-Regular.ttf"),
"nunito-bold": require("./assets/fonts/Nunito-Bold.ttf"),
});
};
In your App.js
import React, { useState } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import { NavigationContainer } from '#react-navigation/native';
import AppLoading from 'expo-app-loading';
import useFonts from "./hooks/useFonts";
import getSecureKey from './utilities/getSecureKey';
import AppNavigator from './navigation/AppNavigator';
export default function App() {
const [IsReady, SetIsReady] = useState(false);
// Always perform Token Restoration in App.js just to keep code clear.
const FontAndTokenRestoration = async () => {
await useFonts(); // Font is being loaded here
const token = await getSecureKey();
if (token) {
console.log(token);
}
};
if (!IsReady) {
return (
<AppLoading
startAsync={FontAndTokenRestoration}
onFinish={() => SetIsReady(true)}
onError={() => {}}
/>
);
}
return (
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
);
}

How Do you access function props in your class component?

I am trying to set up react redux, also i have set it but I am getting an error TypeError: _this.props.showLoader is not a function. (In '_this.props.showLoader(true)', '_this.props.showLoader' is undefined).
this.props.showLoader(true)
This function has been defined in Action.js file you can find it below.
This error comes whenever I try to call a function from Root.js file, you can find it below.
Below is the code what I have done so far:->
I have App.js file in which I have set for provider and store
import React, { Component } from 'react';
import {StyleSheet, View, Text} from 'react-native';
import {Provider} from 'react-redux';
import store from './src/redux/Store'
import Root from './src/root/Root'
function App() {
return (
<Provider store={store}>
<View style={localStyles.container}>
<Root/>
</View>
</Provider>
);
}
export default App;
const localStyles = StyleSheet.create({
container: {
flex: 1,
},
});
This is the Navigation Route File
import React from 'react'
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import TutorialSwipeScreen from '../components/viewcontrollers/onboarding/TutorialSwipeScreen'
const Stack = createStackNavigator();
function goStack() {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false
}}
>
<Stack.Screen name="Login" component={Login Screen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default goStack;
My Root.js file
where I am trying to this.props.showLoader it gives me error
import React, {Component} from 'react'
import {View, StyleSheet, Text} from 'react-native'
import NavigationRoute from './NavigationRoutes'
import DeviceInfo from 'react-native-device-info'
class Root extends Component {
constructor(props) {
super(props)
console.warn('props=', this.props)
}
componentDidMount() {
this.call()
}
call = () => {
this.props.showLoader(true)
}
render () {
return (
<View style={styles.rootContainer}>
<NavigationRoute/>
</View>
)
}
}
export default Root;
const styles = StyleSheet.create({
rootContainer: {
flex: 1,
}
});
My RootContainer.js File
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'
import * as Actions from '../redux/Actions'
import Root from './Root'
function mapStateToProps(state) {
return {
shouldShowLoader: state.dataReducer._showLoader,
shouldChangeCounting: state.dataReducer.counter
}
}
function mapDispatchToProps(dispatch) {
const mergedActions = Object.assign({}, Actions);
return bindActionCreators(mergedActions, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Root);
My Store.js file
import {createStore} from 'redux'
import reducers from './RootReducer'
export default createStore(reducers);
My RootReducer.js
import {combineReducers} from 'redux'
import dataReducer from './reducers/Reducer'
const rootReducer = combineReducers ({
dataReducer,
})
export default rootReducer;
My Action.js file which has a function showLoader(bool) which I try to call from Root.js which gives me an error as I have quoted above.
export const DISPLAY_LOADER = 'DISPLAY_LOADER';
export const REFRESH = 'REFRESH';
export const COUNTER = 'COUNTER';
export function showLoader(bool) {
return {
type: DISPLAY_LOADER, data: bool,
};
}
export function refresh() {
return {
type: REFRESH, data: true,
};
}
export function counting(count) {
return {
type: COUNTER, data: count
}
}
And Finally Reducer.js file code goes here
import { DISPLAY_LOADER, REFRESH, WELCOME_POPUP, LOGIN_RELOAD, MESSAGE_POPUP, LOGOUT, COUNTER} from '../Actions';
const initialState = {
counter: 5,
_showLoader: false,
_showMessagePopup: false,
_loginReload: false,
_refresh: false,
_heading: 'Message Heading',
_message: 'PWG Custom Message',
}
const dataReducer = (state = initialState, action) => {
switch(action.type) {
case DISPLAY_LOADER: {
return {...initialState, _showLoader: action.data}
}
case REFRESH: {
return {...initialState, _refresh: action.data}
}
case LOGOUT: {
return {...initialState, _refresh: true}
}
case COUNTER: {
return {...initialState, counter: action.data}
}
default: {
return state;
}
}
}
export default dataReducer;
So where my mistake is I am not able to find with what lines of code I am receiving error at my end. Please help. Also I am new to react native please bear with me.
Thanks
I have got the answer, I am answering my own post.
In the App.js File You need to import
RootContainer
instead of
Root
That’s it and it works Voila.
import RootContainer from './src/root/RootContainer'
I see that your showLoader tied to your redux reducer.
what i can suggest you, you should return state not initialState in your reducer. Basically what youre doing is after each change of state in redux, everything else will go back to its initialState but not the changed state itself.
You also didn't pass props showLoader to your Root component. If your intention is using showLoader as a setState function for your Root component, you should define the function in your mapDispatchToProps in your connect function

I can't navigate without the navigation prop of react-navigation with react-i18next container

I apply the navigation example without the navigation prop of the react-navigations docs (NavigationService), but I can't make it work with react-i18next.
I applied the example of the documentation https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html in my code:
// App.js
import React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/lib/integration/react';
import { createAppContainer } from 'react-navigation';
import { I18nextProvider, withNamespaces } from 'react-i18next';
import { persistor, store } from './src/store';
import I18n from './src/localization';
import Navigation from './src/navigation';
import NavigationService from './src/navigation/NavigationService';
import Loader from './src/screens/components/Loader';
class NavigationStack extends React.Component {
static router = Navigation.router;
render() {
const { t } = this.props;
return <Navigation screenProps={{ t, I18n }} {...this.props} />;
}
};
const ReloadNavOnLanguageChange = withNamespaces(['common', 'server'], {
bindI18n: 'languageChanged',
bindStore: false,
})(createAppContainer(NavigationStack));
export default class App extends React.Component {
...
render() {
return (
<Provider store={store}>
<PersistGate loading={<Loader />} persistor={persistor}>
<I18nextProvider i18n={ I18n } >
<ReloadNavOnLanguageChange ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}} />
</I18nextProvider>
</PersistGate>
</Provider>
);
};
};
// Navigation.js
...
export default Navigation = createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
Login: LoginScreen,
App: AppScreen
},
{
initialRouteName: 'AuthLoading'
}
);
// NavigationService.js
Apply the same code that's in https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
// Any JS module of my project (actions, helpers, components, etc.)
import NavigationService from 'path-to-NavigationService.js';
...
NavigationService.navigate('Login');
When the authorization token is validated and the result is negative, the login screen must be opened (NavigationService.navigate('Login')) but it returns the error _navigator.dispatch is not a function in NavigationService.js:
const navigate = (routeName, params) => {
// DISPATCH ERROR
   _navigator.dispatch(
     NavigationActions.navigate({
       routeName,
       params
     })
   );
};
Dependencies:
react 16.5.0
react-native 57.1
react-i18next 9.0.0
react-navigation 3.1.2
Any suggestion? Has anyone else found this scenario?
Using the innerRef option of the withNamespaces hoc of react-i18next instead of passing the function through the ref property of the root component… AS THE DOCUMENTATION OF REACT-I18NETX SAYS!
// App.js
...
const ReloadNavOnLanguageChange = withNamespaces(['common', 'server'], {
bindI18n: 'languageChanged',
bindStore: false,
innerRef: (ref) => NavigationService.setTopLevelNavigator(ref)
})(createAppContainer(NavigationStack));
export default class App extends React.Component {
...
render() {
return (
...
<ReloadNavOnLanguageChange />
...
);
};
}