Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot navigate to same route with different params #2882

Closed
Gerharddc opened this issue Nov 2, 2017 · 44 comments
Closed

Cannot navigate to same route with different params #2882

Gerharddc opened this issue Nov 2, 2017 · 44 comments

Comments

@Gerharddc
Copy link

Current Behavior

  • When I am currently on a specific screen and I try to navigate to the same screen but with different params then no navigation seems to take place and the current screen just gets the new params.
  • This means that there is no animation to show navigation and when you try to go "back" then you go back to what was before the first instance of this screen.

Expected Behavior

  • If I dispatch an action to navigate to an existing route but with different parameters then I expect it to push a new route to the stack instead of just altering the params of the current one.

Other details

  • I need this for something like a user info screen as it is possible that you may want to navigate from the screen of one user to that of another and be able to come back.
  • I am using Redux to manage state and am using the following code to generate and then reduce the action in the appropriate places:
    • NavigationActions.navigate({ routeName: 'UserInfo', params: {userId} });
    • const newState = AppNavigator.router.getStateForAction(action, state);
  • I have even tried generating the actions by using route names but that does not change anything.
  • My only other option is to create a custom reducer but that seems tedious and from what I have read on other issues, the system should actually already work like I expect it to.

Your Environment

software version
react-navigation 1.0.0-beta.15
react-native 0.49.5
node 8.8.1
npm or yarn yarn 1.2.1
@kelset
Copy link

kelset commented Nov 2, 2017

Currently, the library does not support idempotent actions (you can navigate to the same screen over and over) but it is part of the new roadmap. #2585. Until this exists in the lib, you'll need to hand-roll a solution.

(closing to reduce redundancy on the subject)

@kelset kelset closed this as completed Nov 2, 2017
@Gerharddc
Copy link
Author

@kelset I don't understand, are you saying that you CAN navigate to the same screen over and over because this is what I want? Or are you saying it is not currently supported because #2533 seems to suggested that it is and what is being worked on is that this will not happen?

@kelset kelset added question and removed question labels Nov 2, 2017
@kelset
Copy link

kelset commented Nov 2, 2017

Sorry (it's a messy topic), I misunderstood your issue. It was actually patched, then reverse, so, at the moment, as far as I can tell, you should be able to push twice the same rote in a Stack.

Can you provide a repo / expo snack that has the behaviour you are experiencing? It's kinda weird that you are not able to do it atm.

@kelset kelset reopened this Nov 2, 2017
@Gerharddc
Copy link
Author

@kelset yes, I am getting a lot of mixed messages.

I saw quite a few issues where people can continuously navigate to the same screen. And a lot of them did not want it because it can apparently happen accidentally. I have not had that though but I would like to be able to navigate to the same screen but with different params. At the moment no new screen is pushed and the current one just gets the new params in-place. I think this is what I a lot of other people wanted.

As I said I am on 1.0.0-beta.15 and I was on 1.0.0-beta.11 where it did not work either. I am not sure on what version I need to be so that the idempotent fix was either never there or has been reversed. I cannot use 1.0.0-beta.16 at the moment though because it is broken as can be seen by some recent issues.

@Gerharddc
Copy link
Author

Ok so I made a basic demo repo but it did not have the problem. After a lot of tweaking, I finally found that this only happens in the big app because the routeName I was using was for a screen inside a TabNavigator and obviously the TabNavigator doesn't push to the stack (facepalm).

@dowmeister
Copy link

dowmeister commented May 3, 2018

It's a bit late, but come into this issue only now and searching on Google landed here.

I've resolved using a "fake" screen that dispatch the navigation, so the stack is filled with different route key:

class NavigationDispachter extends BaseTruckyScreen {
    // eslint-disable-line

    constructor(props)
    {
        super(props);
    }

    componentDidMount()
    {
        this.props.navigation.navigate(this.props.navigation.state.params.to, this.props.navigation.state.params);
    }

    render() {
        return (
            <View />
        );
    }
}

import { globalMapStateToProps } from '../reducers';

export default connect(globalMapStateToProps, null)(NavigationDispachter)

Navigate to this route with this:

this.props.navigation.navigate('NavigationDispatcher', { to: 'UserProfile', steamID: row.account.steam_id, goBack: 'MyProfile' })

hope this helps.

@marvincreat
Copy link

marvincreat commented May 26, 2018

https://reactnavigation.org/docs/en/navigation-key.html#usage-with-the-navigate-navigation-actionshtml-navigate-call
@Gerharddc userful for big app with TabNavigator

             const navigateAction = NavigationActions.navigate({
                 routeName: 'APage',
            
                params: {
                     title: title,
                     name: 'APage',
                 },
                 key: 'APage' + APageUUID
            });
            
            this.props.navigation.dispatch(navigateAction);

or

            this.props.navigation.navigate({
                routeName: 'APage',
                params: {
                    title: title,
                    name: 'APage',
                },
                key: 'APage' + APageUUID
            })

@dzpt
Copy link

dzpt commented Jun 2, 2018

@marvincreat thank. Using key helps to solve this trouble

@ltsharma
Copy link

ltsharma commented Jun 8, 2018

@marvincreat i didn't get that APageUUID

@KianooshSoleimani
Copy link

KianooshSoleimani commented Jun 12, 2018

you should just add key to your navigation props like this :

this.props.navigation.navigate ({ key: Math.random () * 10000, routeName: routeName, params: params })

it work for me, hope help you

@sagunkho
Copy link

sagunkho commented Jun 15, 2018

@ltsharma It's just a unique ID that is generated to help the stack differentiate between screens with the same routeName.

I'm using something like this -

	this.props.navigation.navigate({
		routeName: 'Category',
		params: { 
			categories: category.children,
		},
		key: 'Category' + category.name
	});

@marvincreat
Copy link

@ltsharma Just like @sagunkho said,I'm using something like this
import uuid from 'uuid';
const APageUUID = uuid.v4();

@brentvatne
Copy link
Member

brentvatne commented Jun 15, 2018

the answer from @sagunkho makes the most sense here - you most likely have some data associated with the screen (an id, at category name, whatever). if you don’t, then there is some static difference and you can statically give it a different route key. if that somehow isn’t true, just use push instead of navigate. push will generate a unique key for you, no need to generate one randomly manually.

@heliocaruccio
Copy link

@marvincreat how do you use navigation.goBack()

@rikkitissier
Copy link

@marvincreat Thanks for that tip, saved me a lot of hassle 👍

@rochapablo
Copy link

rochapablo commented Sep 22, 2018

None of this seems to work for me.

this.props.navigation.navigate({
      routeName: 'Drawer',
      params: { category: 1 },
      action: NavigationActions.navigate({ routeName: 'Home' }),
      key: uuid.v4()
    });
this.props.navigation.navigate({
      routeName: 'Home',
      params: { category: 1 },
      key: uuid.v4()
    });
"react-navigation": "^2.14.2",
"react-native": "~0.55.2",
"react": "16.3.1",

@robclouth
Copy link

@rochapablo Maybe it doesn't work in v2

@drash-course
Copy link

For those that face the same issue in v2, @sagunkho solution worked.

this.props.navigation.navigate({
  routeName: MyScreenName,
  key: `${MyScreenName}-step${this.params.step + 1}`,
})

@xiamencloud
Copy link

When i use this.props.navigation.navigate({ routeName:"A", params:{id: 1}, key:uuid.v4() });
It tips me : setParams cannot be called by root navigator?

@beaur
Copy link

beaur commented Nov 28, 2018

@xiamencloud I had the same issue - seems in my case I was using numbers for the key instead of a string and changing to a string seemed to solve it. I imagine uuid.v4() returns a string though so not sure in your case.

@rogsmith
Copy link

Any other suggestions? I am having this same issue in "react-navigation": "^3.0.9"

@Freir96
Copy link

Freir96 commented Jan 15, 2019

I tryed tu use:

NavigationActions.navigate({
            routeName,
            params,
            key: key
        })

but I get

Invariany Violation: setParams cannot be called by root navigator

Edit: It was caused by using randm number as key.

@ishabo
Copy link

ishabo commented Feb 18, 2019

I have the same problem. Pushing to the same screen with different params does not push the same screen onto the stack, and so I don't get the screen slide animation nor can I go back to the previous same screen that has the different params.

I am using react-navigation 3.0.9 with react-native: 0.58.4

@kirkdrichardson
Copy link

In react-navigation 3+ you should use navigation.push to navigate to the same route with different params, essentially forcing a 'rerender'.

From the docs

<Button
  title="Go to Details... again"
  onPress={() => this.props.navigation.push('Details')}
/>

This allows us to express the intent to add another route regardless of the existing navigation history.

https://reactnavigation.org/docs/en/navigating.html#navigate-to-a-route-multiple-times

@sungsong88
Copy link

@kirkdrichardson omg thank you!! It's cleaner and makes so much more sense now!
I just realized that I should stop using 'navigate' for any pushing screen. (So I only 'navigate' just when I really need to navigate based on the routes!!)

@kirkdrichardson
Copy link

@sungsong88 Yeah, it really depends on your setup. The navigate function does provide some benefits. Say I have 3 screens A, B, and C. I start on screen A and navigate to screen B, then to screen C. My navigation route history stack now looks like [A, B, C].

Here is where it gets tricky. If I navigate to screen B at this point, react-navigation will see that I just came from screen B, and pop the last screen (i.e. screen C) from the stack so that my route history now looks like [A, B]. If screen B doesn't take any params and will always look the same regardless of how you navigate to it, this is probably what we want. The benefit is that B has already been rendered, and by essentially just navigating back, performance is improved and our route history is cleaner.

On the other hand, if my route history is [A, B, C], and I push to B, then my route history will become [A, B, C, B]. We do more render work, but we can ensure that the first B is different than the second B.

In our case, this was important because we had Profile component that took an id in params. Without using push, there was no guarantee that you'd see the correct profile!

Hopefully that didn't make it more confusing 😆

@Shadoworker
Copy link

I fixe this by navigating to my screen like this :

@onAction(){

  //Navigate to my screen
   this.props.navigation.navigate('myScreen', {data : _data })
 //Dispatch my data to my screen 
const setParamsAction = NavigationActions.setParams({
    params: { data: _data  },
    key: 'myScreen',
  });
this.props.navigation.dispatch(setParamsAction);

}

....And in my component "myScreen" , i handle params changes inside componentDidUpdate() method.

Hope this helped

@jorisw
Copy link

jorisw commented Jun 10, 2019

For those trying to deeplink into their app with a URI scheme, while the app is already at the desired path, (e.g. /login to /login?token=abcdef), this problem can arise too, though easily fixed by using componentDidUpdate to check if the params were added or changed:

  componentDidUpdate(){
    const { params } = this.props.navigation.state;
    const { theDesiredStateItem } = this.state;

    // Prevent a setState loop by checking your local state first,
    // then set the desired state based on the params.

    if(!theDesiredStateItem && params && params.token){ 
      this.setState({
        theDesiredStateItem: true,
        someToken: params.token,
      });
    }
  }

@dowmeister
Copy link

i think all approaches dont work with DrawerNavigator.

Anyone could confirm? I'm getting crazy...

@sanchitos
Copy link

Is not working for Drawer Navigator.

I have this:

            <Button
              block
              style={{ margin: 10 }}
              onPress={() => {
                this.props.navigation.navigate('ScreenA', { someParam: 'test'});
              }}
              info
            >
            </Button>
            <Button
              block
              style={{ margin: 10 }}
              onPress={() => {
                this.props.navigation.navigate('ScreenA', {someParam: 'test2'});
              }}
              light
            >

If I press the first button I get "test". If I press the second button I still get "test".

@AleksandarSavic95
Copy link

this.props.navigation.navigate('ScreenA', { someParam: 'test'})

@sanchitos for the first button change it to:

this.props.navigation.navigate({
  routeName: 'ScreenA',
  params: {
    someParam: 'test'
  },
  key: 'ScreenA-test' // value is ScreenA-test
});

For the second button, use:

this.props.navigation.navigate({
  routeName: 'ScreenA',
  params: {
    someParam: 'test2'
  },
  key: 'ScreenA-test2' // different value!
});

@luigbren
Copy link

@ltsharma It's just a unique ID that is generated to help the stack differentiate between screens with the same routeName.

I'm using something like this -

	this.props.navigation.navigate({
		routeName: 'Category',
		params: { 
			categories: category.children,
		},
		key: 'Category' + category.name
	});

thanks man.. for your solution.. perfect!!

@deepakaggarwal7
Copy link

this.props.navigation.replace({....}) instead of navigate worked for me

@HeroSony
Copy link

In react-navigation 3+ you should use navigation.push to navigate to the same route with different params, essentially forcing a 'rerender'.

From the docs

<Button
  title="Go to Details... again"
  onPress={() => this.props.navigation.push('Details')}
/>

This allows us to express the intent to add another route regardless of the existing navigation history.

https://reactnavigation.org/docs/en/navigating.html#navigate-to-a-route-multiple-times

Hi @kirkdrichardson , I am using pusing the same screen, anyway after new screen take inplace I need to call a http request to update new data depend on user's pressing.
For example:

  • When press on user1 -> push new screen1 -> http request -> show user1 data.
  • When press on user2 -> push new screen2-> http request -> show user2 data.

The problem is after press back from screen2, the screen1 show user2 data instead. it's look like it overide screen1 data.
So how to overcome this approach?

@chintanbawa
Copy link

@HeroSony, I am facing the same issue. Have you found any solution for this?

@HeroSony
Copy link

HeroSony commented Aug 24, 2020

@chintanbawa I don't think my implementation is follow the right one but it works. If there are a better solution I will consider to pick that instead. Here it is:

  1. For e.g., on HomeScreen navigate to screen using push
this.props.navigation.push('Profile', { id: profileId });
  1. Fetch detail user1 and friend list on didmount method
componentDidMount() {
    const { params } = this.props.navigation.state;
    if (params && params.id) {
       this.props.getUserDetail(params.id);
    }
  }
  1. Set new user info to route param
componentDidUpdate(prevProps, prevState) {
    if (
      this.props.userProfile &&
      props.userProfile !== this.props.userProfile
    ) {
      const { navigation } = this.props;
      let userProfile = navigation.getParam('userProfile');

      if (!userProfile) {
        navigation.setParams({
          userProfile: this.props.userProfile,
        });
        
      } else {
        // userProfile exist on navigation and need to update it's property
        const { data } = this.props.userProfile; // From http request after update
        if (userProfile.data.id === data.id) {
          navigation.setParams({
            userProfile: this.props.userProfile,
          });
        }
      }
    }
  }

@chintanbawa
Copy link

Thanks @HeroSony for your reply and explanation.

@chintanbawa
Copy link

chintanbawa commented Aug 24, 2020

Hi @HeroSony, I have found the problem.

Problem: Screen 1 is not updating because componentDidMount not get called when Screen 1 regains focus (i.e. when user come back to Screen 1 from Screen 2).

Solution: To fix the problem rather than using componentDidMount I have used onWillFocus from NavigationEvents. onWillFocus gets called every time Screen 1 gains focus.

I hope this will help you and others. Thank you once again.

@sparcbr
Copy link

sparcbr commented Jan 28, 2021

Anyone getting below error message when trying to navigate using a random key?

The action 'NAVIGATE' with payload {...} was not handled by any navigator

Relevant app code:

const Tab = createBottomTabNavigator()

export const App = () => {
	return (
		<NavigationContainer>
			<Tab.Navigator initialRouteName="Tabela">
				<Tab.Screen name="Coletar" component={CollectsScreen} />
				<Tab.Screen name="Tabela" component={CollectReportScreen} />
			</Tab.Navigator>
		</NavigationContainer>
	)
}

In CollectReportScreen I call:

navigation.navigate({
	routeName: 'Coletar',
	params: {
		date: collect.date.getTime(),
		commission: collect.commission,
		id: collect.id,
	},
	key: Number(Math.random() * 10000).toString()
})

Which gives the error:
The action 'NAVIGATE' with payload {"routeName":"Coletar","params":{"date":1606945741554,"commission":78,"id":2,"key":"1580.8629305620937"} was not handled by any navigator.

Environment

@react-navigation/bottom-tabs 5.11.7
@react-navigation/core 5.14.4
@react-navigation/native 5.8.10
react-native 0.63.46
react-native-screens 2.16.1
node v12.19.0
npm 6.14.9

@conversantech
Copy link

@sparcbr same hear

@conversantech
Copy link

conversantech commented Mar 3, 2021

@sparcbr

this works for me :
this.props.navigation.push('User', {userId:'paramValue'});

reference: reference

@anlai2
Copy link

anlai2 commented Mar 13, 2021

Anyone have a solution for this in v5? Haven't ran into a fix yet.

@joix97
Copy link

joix97 commented Apr 17, 2021

@anlai2

in some NavigationService you can do:

import * as React from 'react';

const routeName = 'ScreenName';
const navigationRef = React.createRef();
navigationRef.current.navigate(routeName, {lastUpd: Date.now()});


in the screen given by routeName

class ScreenNameComponent extends React.Component<Props> {
  state = {
    lastUpdate: null
  }

componentDidUpdate() {
    const { params } = this.props.route;
    if (params && params.lastUpd) {
      if (params.lastUpdate != this.state.lastUpd)
      {
        this.setState({lastUpdate: params.lastUpd});
        this.props.downloadData();
      }
    }
  }
...
}
...
export { ScreenName };

@github-actions
Copy link

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests