I'm using react-navigation in my react native app.
In two different places I a have button to navigate to a route. One button also sends params and one button does not:
<Button onPress={() => { navigation.navigate('profile', { user: 'test' }) }} title="test" />
<Button onPress={() => { navigation.navigate('profile') }} title="test" />
In profile route I have a componentWillMount function to set state according to received params:
componentWillMount() {
const { params } = this.props.navigation.state;
if (params && params.hasOwnProperty('profile')) {
this.setState({
profile: params.profile
});
}
}
The problem is that if I press the first button (the one that sends params), then navigate back to the same view and press the second button, I'm still receiving the same params, even though the second button isn't sending any!
Not by my computer so I can't test.
But what if you navigate with the second button first? Do you still get the params passed to the page?
Also you dont mention what type navigation you sre navigating to. A stack, drawer or tab. Add some more info and I'll try to come back to you tomorrow.
But my impression is that you are navigating to the same page the second time and that page is already mounted. And hence not rerendered. Have you tried with componentDidUpdate an passing no variables on the second button?
This is the change I suggest in our discussion:
if (params && params.hasOwnProperty('profile')) {
this.setState({
profile: params.profile
});
else if (params && params.hasOwnProperty('profile')){
this.setState({
profile: null
});
}
Related
I have a root screen called selectlanguage once user select his language I'm passing that selected language as params to the Home screen, and then to "Home" children. but once I hit back button from that child screen to the Home that param turns to undefined that why when I want to open that child screen again it cause some issues.
Root screen selectedLanguage:
<View style={{ marginTop: 40 }}>
<Button
handleClick={() =>
navigation.navigate('Home', { lan: userLanData.value })
}
title={'NEXT'}
{...}
/>
Home screen:
const lan = route.params?.lan;
// and below i'm passing above param as props to Task screen
<Tasks
lanParam={lan}
{...}
/>
Task screen:
//Again passing it to another Child screen
<TouchableOpacity
onPress={() =>
navigation.navigate('byWords', {
id: item.id,
currentLevelName,
index,
lanParam, <-----
})
}>
{...}
</TouchableOpacity>
Task Child screen:
const lanParam = route?.params?.lanParam;
const colRef = collection(
db,
`${lanParam && lanParam}/${currLevelParam}/tasks/${id}/byWords/`
);
And from this point I had an idea to try send this Param back to home screen while coming back like this:
<Header
{...}
goBack={() =>
navigation.navigate('Home', { index, currLevelParam, lanParam })
}
/>
And now Home screen should look like this and I'm receiving that value back again:
Home screen:
const lan = route.params?.lan;
const lanParam = route.params?.lanParam;
// and below i'm passing above param as props to Task screen
<Tasks
lanParam={lan}
{...}
/>
But the problem is that, how could I use that value again since we have no access to the root screen again ?
it was easy to fix!!
I just modified lanParams to lan to keep same as Original param and update Home param again while coming back
Using react-navigation I am doing stuff inside one of screens, where I display items, and optionally delete them. When deleting, I need to update the screen, which I am having trouble with. I tried navigating to the same screen or updating useState hook variables, which should re-render the screen but dont. For now, I am refreshing it in the dumbest way: I delete the item, navigate to other screen, timeout 5ms and go back to the screen that I want to update.
function ListScreen({navigation}) {
<FlatList>
...stuff
<ListItem.Chevron onPress={ () => Alert.alert('Alert Title', 'Do you really want to delete this?',
[{ text: 'YES', onPress: () => deleteAndRefresh(id) }
/>
function deleteAndRefresh(id) {
deleteProduct(id); //database call
navigation.navigate('Home');
setTimeout(function () {
navigation.navigate('List');
}, 5);
}
}
How could I refresh the screen inside deleteAndRefresh more ... elegantly?
Did you try push ?
navigation.push('List');
I am making a test with react navigaton and webview , I have 2 screens , Home and Details , at details screen I am calling / opening a webpage inside webview , let's say that I am calling stackoverflow.com (Page A) , my problem is that when user click a link of the stackoverflow page and navigate and after wants to go back to the previous page (Page A) , it doesn't go , its going or navigating to the Home screen !!!
how can I let The user go back to the previous page. ?
that 's how I am calling the page
<WebView
javaScriptEnabled
source={{uri: 'https://stackoverflow.com/'}}
style={{marginTop: 20}}
/>
As we know built in back button is not provided in iOs but it is provided in android .
So for considering both platform there is two possibility.
Android.
-> For android you have to define BackHandler so here are the step.
import it like this.
import {BackHandler } from 'react-native'.
initialize backhandler inside the life cycle methods.
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
}
handleBackPress = () => {
if (this.state.canGoBack) {
this.refWeb.goBack();
}
else{
this.props.navigation.goBack(null)
}
return true;
}
define a userdefine variable canGoBack inside the status.
constructor(props) {
super(props)
this.state = {
canGoBack: false
}
}
create a method which detect the change in navigation of the webview and bind it with the web view.
onNavigationStateChange(navState) {
this.setState({
canGoBack: navState.canGoBack
});
}
Bind it like this.
<WebView
ref={(myWeb) => this.refWeb = myWeb}
onNavigationStateChange={this.onNavigationStateChange.bind(this)}
source={{ uri: 'https://stackoverflow.com/questions/51712310/react-
native-webview-navigation-issue' }} />
And thsts it you are ready to go..
iOs
For iOs you didn't have to bother too much.
Create a button for back press above the webview or according to your design logic
Follow the above webview and navigation logic . forgot about the backhandler and set this code inside the onPress() method of your created button of backpress
if (this.state.canGoBack) {
this.refWeb.goBack();
}else{
this.props.navigation.goBack(null)
}
Note : Here I use stackNavigator for screen navigation so i used this.props.navigation.goBack(null) this code. if you didn't use it then dont consider this code and replace with your feasible navigator code in else condition
Thankyou..
My Problem is that I would like to navigateBack() from the BountyDetailsScreen to the LoyaltyScreen, but the navigateBack() function call does not trigger any action. When I log the function it says:
The only thing I notice is, that the navigationStack is empty. When I do the same with the navigateTo function it is working, but then I have a messed up navigation stack.
In my LoyaltyScreen.js I am displaying a ListView. It is a RN ListView (not imported from shoutem).
LoyaltyScreen.js
renderRow(bounty) {
return (
<ListBountiesView
key={bounty.id}
bounty={bounty}
onDetailPress={this.openDetailsScreen}
redeemBounty={this.redeemBounty}
/>
);
}
ListBountiesView.js
The ListBountiesView renders each ListView Row and opens a Detail Screen when clicked on the Row.
render() {
const { bounty } = this.props;
return (
<TouchableOpacity onPress={this.onDetailPress}>
{bounty.type == 0 ? this.renderInShopBounty() : this.renderContestBounty()}
<Divider styleName="line" />
</TouchableOpacity>
);
}
BountyDetailsScreen.js
In the BountyDetailsScreen I display detailed information and would like to navigateBack() to the Loyalty Screen when I press a button.
<Button styleName="full-width" onPress={() => this.onRedeemClick()}>
<Icon name="add-to-cart" />
<Text>Einlösen</Text>
</Button>
onRedeemClick() {
const { bounty, onRedeemPress } = this.props;
onRedeemPress(bounty);
navigateBack();
}
navigateBack is an action creator. You need to map it to props and read it from props in your redeemClick function. Just executing the imported action creator won't do anything since it's not connected to Redux.
Here's an example of you map it to props:
export default connect(mapStateToProps, { navigateBack })(SomeScreen));
Here's how you use it:
const { navigateBack } = this.props;
navigateBack();
I can see that airmiha's answer is what you're looking for, but I just wanted to add onto it.
You can also use hasHistory to set up your #shoutem/ui NavigationBar (if you're using it) with a simple back button that utilises navigateBack().
<NavigationBar
styleName="no-border"
hasHistory
title="The Orange Tabbies"
share={{
link: 'http://the-orange-tabbies.org',
text: 'I was underwhelmed by The Orange Tabbies, but then I looked at that
sweet, sweet back button on the Nav Bar.
#MakeNavBarsGreatAgain',
title: 'Nevermind the cats, check the Nav Bar!',
}}
/>
You can find more examples with the NavigationBar component here.
Let's say I have this screen:
And when the user clicks on the white tooltip, it redirects to another screen. Sometimes the app lags a little bit, and clicking on the tooltip takes like ~2s to see the screen change. The problem is, during those 2s, the user taps again on this tooltip to make it happen.
And the result I get is that there are two instances of the new screen in my StackNavigator. What I mean is that I see my new screen, but when I click on "Back" I don't return to this 'Hitchhiking Map' screen, but to another instance of that same screen.
If I clicked 5 times on the callout during those 2s, then I need to click 5 times "Back" to return to the Map screen. Any way to prevent that? To put only one instance into the StackNavigator?
I am using React Navigation, more precisely a StackNavigator. Here's my code:
The "click on tooltip" part:
<MapView.Marker
onCalloutPress={() => this.props.navigation.navigate('spotDetails', { spotId: marker.id })}
/>
My screens:
const HMapNavigator = StackNavigator({
HMap: { screen: HMapViewContainer },
spotDetails: { screen: SpotDetailsViewContainer },
});
The issue of multiple navigations has been reported and there is more detail here.
react-navigation (v1.0.0-beta.7) is in beta and still being built, so until this feature is implemented, you will have to handle the debounce manually.
options
disable the button once the navigation starts
debouncing in the onPress logic or in the action if you are using redux
lodash provides a useful debounce utility, if you are looking for one.
There are many ways how to overcome double navigations.
My solution is adding a key property in navigate object:
this.props.navigation.navigate({ key: 'AnotherScreen', routeName: 'AnotherScreen', params: { ... }})}
Save if its opening to control the navigation
constructor (props) {
super(props)
this.state = {
opening: false
}
}
Create a function to control de navigation
_open (campaign) {
if (!this.state.opening) {
this.props.navigation.navigate('Campaign', {campaign})
this.setState({ opening: true })
setTimeout(() => {
this.setState({ opening: false })
}, 1000)
}
}
Call this func from your onpress
<TouchableHighlight underlayColor='transparent' onPress={this._open.bind(this, campaign)}>
In versions 1.x of React Navigation, if you specify an identifier the navigation action will be executed only once. E.g.:
navigate({ routeName: 'someScreen', key: 'someScreen', params: { someParam: 'someValue' } })
More information: https://gist.github.com/vonovak/ef72f5efe1d36742de8968ff6a708985
In versions 2.x and 3.x this is a default functionality.
More info: https://reactnavigation.org/docs/en/navigation-key.html