How can I receive a variable back in my routes.js to load a different view? - react-native

I am using a switchNavigator to display either a show view or a view where the user can add more content. I want to send back a boolean variable just as a flag, I think I have that part just right but I don't know how to make it so that my code receives it and changes view.
This is in my routes.js
let hasItems = true;
const ItemsScreens = createSwitchNavigator(
{
Items: {
screen: Items,
},
ItemsExist: {
screen: ExistingItems,
},
},
{
mode: 'card',
initialRouteName: hasItems ? 'ItemsExist' : 'Items',
navigationOptions: {
drawerIcon: getDrawerItemIcon('account-balance-wallet'),
title: `Items`,
},
},
);
inside my ExistingItems.js I have a button that does:
<Button
onPress={() => this.props.navigation.navigate('Items', {hasItems: false})}
label={'Add Items'}
/>
My idea is to call the view again but send the false value in the variable to enter the actual adding items state but I have no idea how to make it actually receive the value. I tried doing an if like:
if(this.props.navigation.state.params.hasItems)
but that is undefined and crashes.

As suggested in react navigation Authentication flows example, create one another screen AuthLoadingScreen, which checks the condition and according to condition navigate to your another screen. However, I also know that extra screen will not good for user UI but it will work around.

Which property is seen as undefined?
Aren't you also controlling the wrong variable? the param you are passing is hasBanks but you are controlling hasItems

Related

Pass params to previous screen on swipe/gesture

I've thoroughly read the documentation on passing params between screens with React Navigation: https://reactnavigation.org/docs/params/
However, all of those examples only work if you are calling navigation.navigate manually and passing the params. For example:
<Button
title="Done"
onPress={() => {
// Pass and merge params back to home screen
navigation.navigate({
name: 'Home',
params: { post: postText },
merge: true,
});
}}
/>
I have a screen with a back button, where I can call navigation.navigate and pass params on button press, like in the example above. However, the user can also swipe from the left to go back to the first screen on Android (and I'm assuming iOS as well).
So, my question:
Is there a way for me to pass the same data to the previous screen when the user swipes to go back (instead of pressing the back button)?
There might be a better way that I am unaware of. However, we can achieve this manually by preventing the default back action and handling this ourselves.
Suppose that you have a screen B and you want to swipe back to go to a screen Home and pass params to Home from B on that swipe action. Then, you can achieve this as follows.
function B({navigation}) {
React.useEffect(() => navigation.addListener('beforeRemove', (e) => {
// the navigation.navigate will fire beforeRemove which causes an infinite loop. we guard this here
if (e.data.action.type === "NAVIGATE") {
return
}
// Prevent default behavior of leaving the screen
e.preventDefault();
// navigate manually
navigation.navigate({
name: 'Home',
params: { post: postText },
merge: true,
});
}), [navigation]);
return ( ... )
}
Edit: The navigation.navigate fires the beforeRemove event again, obviously, which causes an infinite loop. We can guard this as shown above.

Route elsewhere on navigate to route

With using the React-Navigation, to go to a specific page I would do this:
navigation.navigate('ProgramNav', {
screen: "Programs"
});
navigation.navigate('ProgramNav', {
screen: "Class List",
params: {
programId,
}
});
navigation.navigate('ProgramNav', {
screen: "Student Items",
params: {
programId,
studentId,
}
});
navigation.navigate('ProgramNav', {
screen: "Student Item View",
params: {
programId,
studentId,
itemId
}
});
So far, it hasn't been much of an issue, but what I want is to be able to make it so that when I navigate to the "Programs" screen, it checks if there is one program. If so, it navigates to the "Class List" with that one program automatically (ideally removing itself from the stack). BUT I want that logic to be in the ProgramsScreen component and not where I do the navigation.navigate.
I also want to make sure that when I hit Back, it will skip the Screen as well.
I am thinking it's something like navigation.replace() when there's something like a useFocusEffect
I got it working with useFocusEffect and replace BUT it does an animation. Is there a way of avoiding that animation?
useFocusEffect(useCallback(() => {
if (programs.length === 1) {
navigation.replace('Class List', {
...route.params,
programId: programs[0].programID
})
}
}, []))
To work around the animation issue at least when I navigate from the tab bar. I had to create a new route and make that the new initial route such that if I navigate to the root page I would route to the proper one using useFocuseEffect as above and apply to the combinations leading up to the screen I need also returning null and headerShown: false to prevent showing any content.

React Native StackNavigator Title Change Causes Recursion

I've split out the navigationOptions to the top of each screen to make things easy to understand, rather than keeping it in the TabNavigator. Here's an example of one screen:
static navigationOptions = ({navigation}) => {
const {params = {}} = navigation.state;
return {
title: params.title != undefined ? `${params.title}` : "Step 1: Select a Letter",
tabBarOnPress: ({...props, scene})=>{
params.onFocus();
},
titleStyle: {
textAlign: 'center'
}
}
};
Once a user selects a letter on this screen, I switch the view to show a different component (a list of items that start with that letter, for example).
I'd like to change the title of the TabNavigator Header to now show something like "Step 2: Select an item from the list". I saw on a thread that you can call a method from the method that rebuilds the view like this:
_letterSelectedHandler = (letter) => {
this._changeTitle("Step 2: Select an item...");
...
}
With the method:
_changeTitle = (title) => {
this.props.navigation.setParams({title});
}
Unfortunately, it seems to create an infinite loop. Is there a better way of simply changing the TabNavigator Header Title?
I should mention that I'm not using Redux or anything too complex, since I'm new to React Native. Please let me know if I can provide any more information to help illustrate the issue I'm having.

React Navigation cache component

I am fairly new to React Native and a little confused with the following. I've set up a StackNavigator as shown
const MyProjectNavigator = StackNavigator({
home: {screen: Other},
latest_news: {screen: LatestNews}
}
When I want to go to a different screenm the navigation I perform is:
navigate(
latest_news, {
otherParams : param1
}
);
This works well so far.
Now, assume that the latest_news component queries a lot of data from a server when mounted, then performs lots of operations on that data, sorting by date, author, yadda yadda. This takes some time to complete.
How would you suggest I made this faster? On iOS for example i would normally keep my ViewController in memory and if it was available, display that. When using navigate(), the navigator seems to create a new instance of the component thus reloading and re-processing everything from the beginning making the users wait every time.
*TL;DR
I want to keep all the data my component has queried and processed across navigation so that the processing doesn't have to repeat constantly.
I could just put the data on the global object but that doesn't sound like a good solution
Thanks a bunch.
You can use the TabNavigator instead of the StackNavigator.
TabNavigators reuse the same instances of its route items.
you can disable the visibility of the TabBar:
const TabNav = TabNavigator({
Home: { screen: HomePage},
NewsFeed1: { screen: NewsFeed} ,
NewsFeed2: { screen: NewsFeed} ,
NewsFeed3: { screen: NewsFeed} ,
// ...
},
{
navigationOptions: ({ navigation }) => ({
tabBarVisible: false,
}),});
and then you can navigate manually - for example with a button:
<Button title="NewsFeed1" onPress={() => this.props.navigation.navigate("NewsFeed1") }/>
Have a look at a simple example:
https://github.com/chrisdie/AwesomeNavigation/blob/master/src/tabbar2/App.js

go two screen back with single press event using react-navigation in react native app

I am using reactnavigation component from https://reactnavigation.org and using the code below i am going one screen back
<Button
onPress={() => goBack()}
title="Go back from this HomeScreen"
/>
how can i go 2 screen back on single press action
I am using this code to initialize the navigator
const RouteConfigs = {
Login: {screen:Login},
Home: {screen:Home},
Chat: {screen:Chat},
Facebook: {screen:Facebook},
Facebookgallery: {screen:Facebookgallery}
}
const StackNavigatorConfig = {
headerMode: 'none',
}
export default StackNavigator(RouteConfigs, StackNavigatorConfig)
I navigate from home to Facebook with this code :
() => this.props.navigation.navigate('Facebook', {user:this.state.user})
and from Facebook to Facebookgallery with this code :
onPress={ () => this.props.navigation.navigate('Facebookgallery', {user:this.state.user}) }
now i want to go back from Facebookgallery to Home directly with some parameters
I know it's an older question but what I use is:
navigation.pop(n);
Takes you to the previous screen in the stack. If you provide a number, n, it will specify how many screens to take you back within the stack.
https://reactnavigation.org/docs/en/navigation-prop.html
you can use navigation.pop(screenCount)
with screenCount as integer
navigation.pop(2)
here is for refference
https://reactnavigation.org/docs/stack-actions#pop
React Navigation is updated not to use pop function directly, use dispatch function with StackActions.
navigation.dispatch(StackActions.pop(2));
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_A });
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_B });
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_C });
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_D });
Now you are on-screen D and want to go back to screen A (popping D, C, and B). Then you need to supply a key to goBack FROM:
navigation.goBack(SCREEN_KEY_B)
https://reactnavigation.org/docs/en/navigation-prop.html#going-back-from-a-specific-screen-with-goback
The "proper" solution right now is:
Get the key for the screen after the screen you want to go to. In
your case, you'll need to get the key for the Facebook screen.
Call NavigationAction.back(key: 'key-of-previous-screen'), and it
will pop your navigation stack as if you were on the screen with that key
Code example where I go back multiple screens in a thunk (relevant if you use redux):
export const postItemAndReturnToMap = item => (dispatch, getState) => {
const {nav} = getState();
// Get the key for the screen after/above the root view and use that as reference
// to return to the root view. This is hardcoded for my stack setup, as you can see
// there are quite a few nested StackNavigators in my setup
const {key} = nav.routes[0].routes[0].routes[0].routes[1];
// Dispatch whatever action you want
dispatch(postItem(item));
// This will now go back multiple screens, in my case to the
// bottom of the top stackNavigator
return dispatch(NavigationActions.back({key}));
};
This isn't pretty. Let's hope react-navigation implements something like .back(2) to make things easier in the future.
If you are using push then you can go to root screen with this
this.props.navigation.popToTop();
and to Push
this.props.navigation.navigate('SummaryScreen', {
otherParam: 'anything you want here',
});
You could define an additional navigation action type, e.g. POP_TWO_ROUTES and overwrite StackRouter.getStateForAction(passedAction, state) like that (only exemplary):
...
const defaultGetStateForAction = AppNavigator.router.getStateForAction;
AppNavigator.router.getStateForAction = (passedAction, state) => {
if(state && state.routes && state.routes.length > 2
&& passedAction.type === 'POP_TWO_ROUTES') {
let routes = state.routes.slice();
routes.pop();
routes.pop();
return {
index: routes.length - 1,
routes: routes
};
}
// default behaviour for none custom types
return defaultGetStateForAction(passedAction, state);
}
...
Then in your screen component you can do something like that:
...
onPress={() => this.props.navigation.dispatch({
type: 'POP_TWO_ROUTES'
})}
...
See also https://reactnavigation.org/docs/routers/#Custom-Navigation-Actions.
It sounds like you're looking for something along the lines of #reset (https://reactnavigation.org/docs/navigators/navigation-actions#Reset).
I haven't used this library, but you likely are trying to go back to the navigation stack's root screen. Other libraries I've used for nav have some sort of popToRoot or something similar.
Try this
onPress={() => {
props.rootNavigation.dispatch(
NavigationActions.reset({
index: 0,
key:null,
actions: [NavigationActions.navigate({ routeName: 'Home' })]
})
)
}}
This should work:
this.props.navigation.pop(2)
For React Native V6 2022
Simple Go Back "goBack"
navigation.goBack()
Go Back tow screen or 3 on specific the name "navigate"
props.navigation.navigate("RouteName")
Navigate to the top screen "popToTop"
navigation.popToTop()