React - Navigating to the same component weird behaviour

Viewed 348

I'm breaking my head over this, the behaviour I'm seeing seems weird for me, though it might make sense to some of you.

Consider this component:

const DishPreparation = ({ dish }) => {
  const [slideIndex, setSlideIndex] = useState(0)
  const sceneRef = useRef(null)
  useKeepAwake();

  return (
    <View style={styles.scene} ref={sceneRef}>
      <View flex={0.12} style={{ marginTop:-10 }}>
        <ProgressSteps activeStep={slideIndex} activeStepIconBorderColor={colors.lightTan} completedProgressBarColor={colors.lightTan} completedStepIconColor={colors.lightTan}>
          {dish.preparationSteps.map((step, index) => (
            <ProgressStep removeBtnRow key={index}/> ))}
        </ProgressSteps>
        ......
  )

Which is being rendered through another component Dish, that can be navigated to. I have some floating component in my app that allows me to navigate to Dish with a specific dish. If I navigate to a dish through it for the first time, or if I navigate there through other component that doing it by pushing the component to the stack, everything works fine. The weird behaviour is when I'm already inside this component with a specific dish, and then navigate through the floating component to a different dish, it's like the old dish is being kept.

Lets say first dish had 3 elements in dish.preparationSteps, and the second one has 4, then dish.preparationSteps.map(step, index) returns only 3 elements instead of 4, but if I render step.someInfo inside the mapping, then I actually see the new dish values.

How is this happening? I'd expect either 4 elements to be returned, or 3 elements but with the old dish values, how is this mixture happening? Also, dont know if it helps but slideIndex keeps its old value, and doesn't reinitialize to 0.

This is how I navigate through the floating component:

navigation.navigate('Tabs', {
                        screen: 'Home',
                        params: { screen: 'Dish', params: { from: '', data: dish } },
                      })

This is how I navigate to it through other some other component(which works as expected)

navigation.push('Dish', {from: 'DishList', data: item})

If any other code is needed I'll be happy to add it.

2 Answers

When using .navigate instead of .push, navigation will look for that screen name ("Dish") and just change navigation params, without remounting component or opening a new screen with it. Usually you can just use navigation.push (like in your last example), but the problem is that you are trying to navigate from some top-level navigator. You can't use .push there because it will push to the outer navigator which doesn't have "Dish" screen. If you want to add another "Dish" screen on top of the existing one in the nested navigator, you need to navigation.push from the Stack Navigator you want to navigate in; it's only that your floating is not located in that Stack Navigator

First thing you can try is to add a unique key to your screen when navigating, e.g.

navigation.navigate('Tabs', {
  screen: 'Home',
  params: { screen: 'Dish', key: dish.id, params: { from: '', data: dish } },
})

so that navigation will compare screens not by the name but by the name and key. This will most likely replace existing Dish screen with a new one

Better solution would be to dispatch navigation action that will drill down to the Stack Navigator you want to navigate in, and dispatch StackActions.push there

And even better solution would be not trying to navigate inside inner navigators from outer navigators

Maybe the getId prop is what you're looking for?

From the React Navigation docs:

In a stack navigator, calling navigate with a screen name will result in different behavior based on if the screen is already present or not. If the screen is already present in the stack's history, it'll go back to that screen and remove any screens after that. If the screen is not present, it'll push a new screen.

You could use the getId prop to push a new screen instead. For example, say you have specified a getId prop for Profile screen:

<Screen name={Profile} component={ProfileScreen} getId={({ params }) => params.userId} />

Now, if you have a stack with the history Home > Profile (userId: bob) > Settings and you call navigate(Profile, { userId: 'alice' }) the resulting screens will be Home > Profile (userId: bob) > Settings > Profile (userId: alice) since it'll add a new Profile screen as no matching screen was found.

Related