React Native Stacknavigator, run code before going back - react-native

Say there are two screens: screenA, screenB; these are managed using StackNavigator.
User clicks on a button in screenA that navigates the user to screenB. ScreenB has a form where they can edit their name, username, and more. I want to make it so that, after the user edits their personal information by editting the default values of TextInput in screenB, the user can just either click the backbutton in the navigation header or swipe left on the screen to return back to screenA.
In that process of returning back to screenA, I want to have a loading circle and not allow the user to return back to A until the the user receives confirmation from the API endpoint that edits the user's personal information that it worked.
User edits information on screenB.
User tries to navigate back to screenA by either clicking the back button in the header or by swiping right.
A loading circle appears, indicating that the app has sent a request to the API to edit their information accordingly. The user has not navigated to screenA at this point.
Request is successful, and the user is taken to screenA.
For me to do this, I need to be able to add some sort of function to the going back navigation that lets me do all this. Does anyone know how I can do this?
NOTE: I've already looked into writing my custom button for the backbutton but that still doesn't solve the problem of swiping right to navigate.

Please provide us with your code so that we can help better, what you need is an async function. Something like this:
async UploadInformation(username, password) {
let response = await fetch(url,{
method: method,
body: JSON.stringify({
'username':username},
'password':password),
headers: headers
});
return response;
}
Then in your backbutton
async back_button(){
//show your loading indicator
let r=await UploadInformation(this.username, this.password);
//remove your loading indicator
this.props.navigation.navigate('A');
}
By using async and await you are telling react to wait for the function to finish its job.

Related

user authentication in a react native app

I'm building a react native app in which users can login. After a user logs in, the server returns a auth token (jwt) which is stored on the device. This all works.
Now, the problem is with authenticating the user whenever something 'happens'.
After the user logs in and the token is stored, he is send to the screen 'Main'. On that screen (and all the other 'secured' screens), I want to check the auth token. I currently have the following function to do this (simplified):
// get the auth token out of the redux store
const authToken = useSelector(state => state.auth.authToken)
// the function to authenticate a user
const authenticateUser = () => {
try {
fetch('http://URL_TO_SERVER/auth/authenticate-user', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + authToken
}
})
.then(response => response.json())
.then(responseJSON => {
if (responseJSON.response === 'OK') {
// user is authenticated
} else {
// user is NOT authenticated
}
})
}
// only run the function once
useEffect(() => {
authenticateUser()
}, [])
This works, but there is a 'lag' (because fetch returns a promise). When I open the app and go to I screen whilst I'm not logged in, the screen is shown for just a second, and after that, I'm redirected to the login screen. So it works, but I'm not sure that this is the correct way to do so.
I could add a 'layer' to each screen and show a loading modal untill the authenticateUser-function has ran, but I don't think that's the correct way to do so.
My question is: what is the best way to secure screens in a react native app? Is the way I'm doing it OK, or should I switch to another method?
Thanks in advance!
EDIT: I have checked the following articles before asking this question.
Authentication with React Native and API backend
https://scotch.io/tutorials/react-native-app-with-authentication-and-user-management-in-15-minutes
You need to come up with better logic for your auth checks. Usually there is no need to check token every time you open a new screen. Even in your example, you log in and then get sent to a Main screen only to check auth token you've just received. Likewise, should user even see "go to main screen" button when user is clearly not logged in? Do you really want to check for token every time new screen opens in the app? What happens if token is valid by the time you open the screen, but, as the time passes and user stays on the same screen, token becomes invalid?
Try this:
create a part of redux store (e.g. reducer) that will handle current authentication state. Simple "isAuthenticated" flag will do for start
you can access this flag from any screen that is connected to redux
when receiving JWT you can read it's expiration date. So, if you refreshed the token 5 minutes ago and it's not going to expire for the next 2 hours, there is no need to check if token is valid, because you can just assume that by comparing expiration date with current date any time you want
use "isAuthenticated" flag to determine whether or not to show the "go to Main screen" button, so that logged out users will not even see that button
if you need even more control, hook into navigation to check where user is trying to navigate to and allow/deny that by checking against "isAuthenticated" flag in redux store. Take a look here and here
create a service to keep track on token expiration dates and refresh tokens ahead of time if needed, or periodically check if current token is valid, and set "isAuthenticated" to false if it isn't. Create wrappers around your network functions (fetch in your example) and notify this service if status 401 is received in response to any request to the server so that the service can either refresh the token and repeat request, or let user know that he was logged out
See what i believe is the best way toachieve it is by adding a splashscreen to the app, and there you can divert logic if the token exists you can redirect to Homescreen or loginScreen.
like this :
in Splashscreen.js
componentDidMount(){
AsyncStorage.getItem('token') ? navigation.navigate('Home'):navigation.navigate('Login')
}
hope it helps.feel free for doubts

Running api fetch on entering screen

I am creating an app in React Native (create react native) and I am attempting to fetch data from an API every time a user transitions to a screen.
For navigation, I am using the React Navigation library with a combination of Drawer and Stack Navigators.
Typically, I have seen fetch handled via the ComponentDidMount() lifecycle. However, when navigating to a screen in a stack navigator, the ComponentDidMount() lifecycle doesn't appear to trigger and the fetch doesn't run.
Ex. user is on post index, then navigates to add post screen, submits and is redirected to view screen (for that post), then clicks back to return to index. Returning to index does not trigger ComponentDidMount() and fetch isn't run again, so results are not updated.
Additionally, sometimes I need to access navigation params to alter a fetch request when navigation between screens.
I originally was attempting to determine screen transition (when navigation params were passed) via componentWillReceiveProps() method, however, this seemed a bit unreliable.
I did more reading and it sounds like I should be subscribing to listeners (via react navigation). I am having a hard time finding recent examples.
Current process (example)
On the desired screen I would subscribe to listener(s) in the componentDidMount() method:
async componentDidMount() {
this.subs = [this.props.navigation.addListener('willFocus', payload => this.setup(payload))];
}
Based of an example, it sounds like its good practice to remove all subs when unmounting the screen, so I also add:
componentWillUnmount() {
this.subs.forEach((sub) => {
sub.remove();
});
}
then I add a setup() callback method that calls whatever fetch methods I require:
setup = (payload) => {
this.getExampleDataFromApi();
};
Additionally, sometimes I need to access Navigation params that will be used in API queries.
I am setting these params via methods in other screens like so:
goToProfile = (id) => {
this.props.navigation.navigate('Profile', {
exampleParam: 'some value',
});
};
It seems like I cannot access the navigation prop via the provided getParam() method when within callback method from a navigation listener.
For example, this returns undefined.
setup = (payload) => {
console.log(navigation.getParam('exampleParam', []));
};
Instead I am having to do
componentDidFocus = (payload) => {
console.log(payload.action.params.exampleParam);
};
Question
I wanted to ask if my approach for handling fetch when navigating seems appropriate, and if not, what is a better way to tackle handling API requests when navigating between screens?
Thanks, I really appreciate the help!

How to lock the app from exit in react native?

I'm trying to make that the costumer will not be able to leave the app, even when he press on the home button.
My app is coded with react native, and I'm looking for solution by the code and not by configuration because this feature should work only for specific users...
Do you have any idea or any guide that can help me with that?
I was looking for the solution for a long time but nothing came up.
Thank you for the help
You can do this using BackHandler events. You have to put this code in your main navigation file. When user press back button they will exit from app u can customize ur logic for that for particular user etc...
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', function () {
BackHandler.exitApp();
}.bind(this));
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress');
}

React-native componentWillMount not calling

I am new in react-native I have two screens in my stack. Login and Home.
I want to go back to login from a button on home.
I am writing this code
this.props.navigation.navigate('loginScreen')
but in login screen componentWillMount method is not calling. I want to reset my form when user come on login screen from home.
Can anyone help me here?
Thanks in advance.
The this.props.navigation.navigate('loginScreen') don't work because you are now in loginScreen.
If you want to restart page this code isn't good. because have a loop!
correct code:
just when navigate to loginScreen from home use:
this.props.navigation.push('loginScreen')
NOT IN "componentWillMount"
To go back from login from home , you should use this.props.navigation.goBack() if the screen is immidiately before home.
Secondly, you should not use componentWillMount since it is deprecated and will be removed from React 17 onwards. Instead use componentDidMount
Since the component is already mounted therefore it won't call the react lifecycle events componentDidMount again. Therefore you should use the react-navigation listeners didFocus event.
didFocus: the screen focused (if there was a transition, the transition completed)
componentDidMount () {
this._onFocusListener = this.props.navigation.addListener('didFocus', (payload) => {
// Perform the reset action here
});
}
Since your Home and Login Screens are both under the same StackNavigator, when you go from Home back to Login the state stays the same as the component doesn't unmount. The recommended way to solve this is using the SwitchNavigator of react-navigation. Read this very helpful part of the documentation below
https://reactnavigation.org/docs/en/auth-flow.html
You may not be familiar with SwitchNavigator yet. The purpose of
SwitchNavigator is to only ever show one screen at a time. By default,
it does not handle back actions and it resets routes to their default
state when you switch away.
The perfect solution is with this.props.navigation.push('Login'), I tried with SwitchNavigator but it doesn't provide navigationOptions for header.

react native redux storing state across multiple pages

trying to learn redux and having issues passing state around to keep a user up to date, main example - create a user on page 1, navigate to page 2, set something on the user and navigate back to page 1.
having to create an action for page2 navigated passing the user and also creating an action for page 1 navigated passing the user back and forth.
what is the correct usage of redux?
user must be passed back and forth to keep app sync
{
page1: user: {...data}} set user details,
page2: user: {...data}} passed user details but able to edit user
details and able to go back to page1
}
user can be accessed globally at any time and updated with action calls
{
userDetails: {user: {...data}},
page1: only page specific state, accesses this.props.userDetails.user
page2: only page specific state, accesses this.props.userDetails.user
}
So normally you'd just subscribe whatever component needed user information to the Redux store, typically with react-redux. In page2 you'd dispatch an action to modify the user in the store, then when the reducer processes those modifications, page1 can update with the new value.
An action for mutating the user might look like this:
{
type: 'SET_USER_EMAIL',
email: 'foo#wombat.com'
}
You'd dispatch that action from your page2 component. It'd be received in the reducer for the user object in the store:
switch (action.type) {
case 'SET_USER_EMAIL':
return {
...state,
email: action.email
}
}
If both pages are subscribed to the Redux store, there's not really a "passing back and forth" of the user value so much as both pages are notified when the store changes, and can update themselves based on those changes. I guess you'd say that each component listens to the store and can take action when it updates.
Navigation is a somewhat separate issue, though it certainly can use Redux. I feel like possibly you might benefit from revisiting some of the core concepts to make things clearer.