20

Hi I am trying to navigate to next component using navigate function. I am using react-navigation for the navigation among multiple components.

Suppose I have index.android.js and DashboardScreen.js component. I am trying to navigate to DashboardScreen.js component from index component.

It is navigating but index component always retain in component stack. when I press back then it opens index.android.js which should not be. Does anyone know how to manage this in react-native. In Android, finish() works for this.

navigate("DashboardScreen");

When I am navigating from SplashScreen to EnableNotification then SplashScreen should be destroyed, if I am navigating from EnableNotification to CreateMessage then EnableNotification should be destroyed and if I am navigating from CreateMessage to DashboardScreen then CreateMessage should be destroyed. As of now no component is being destroyed.

index.android.js

class SplashScreen extends Component {
  render() {
    if (__DEV__) {
      console.disableYellowBox = true;
    }

    const { navigate } = this.props.navigation;

    AsyncStorage.getItem("@ProductTour:key").then(value => {
      console.log(value);
      if (value) {
        navigate("DashboardScreen");
      }
    });

    return (
     ....
    );
  }
}

const App = StackNavigator(
  {
    Splash: {
      screen: SplashScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    EnableNotification: {
      screen: EnableNotificationScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    CreateMessage: {
      screen: CreateMessageScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    DashboardScreen: {
      screen: DashboardScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    }
  },
  {
    initialRouteName: "Splash"
  }
);
N Sharma
  • 33,489
  • 95
  • 256
  • 444

7 Answers7

12

Just use 'replace' in place of 'navigate'

this.props.navigation.replace('Your Next Component Name')
Surender Kumar
  • 1,152
  • 16
  • 17
4

First of all, using AsyncStorage in an a synchronous function (most especially a lifecycle one) is such a bad idea. You should typically keep ASyncStorage to places in your folder / app structure that make sense for where you access/keep data but since that's not the question I will just mention it quickly here...

Basically you are asking to navigate once the ASync method completes itself based on EVERY render... Those new to RN should know that an awful lot of things can cause a render to fire. Some cases, the render function can fire (I have seen this many times before) 10 or more times before finalizing the last render. This means you would have fired that ASyncStorage method 10 times... definitely something to think about when implementing this stuff. So more or less, the .then(); part of the AsyncStorage function is firing long after the render has already finished doing it's thing. If it was a reasonable approach to use I would say to put the return part of the render function inside of the .then((value) => { return ( ... ); });. But this is an even worse idea. Basically you need the right lifecycle method here and it's NOT the render method.

Anyway, since I have never used this component library before I can only help nudge you in the right direction so here goes... These docs on their webpage seem to say that you need a reference to the props navigator passed down to the component in which you are using it. So if you created the navigator in this class, you would use this.refs.whateverYouNamedTheNavigatorReference.navigate('SomeItemName'). If you are in the class that has been passed this navigator as a prop, you use this.props.passNavigatorPropName.navigate('SomeItemName'). I see you are using variable deconstruction to get the navigate callback but I would caution on doing this, this way because I have seen it cause errors by grabbing an old version of the navigate function or its parent reference by accident and causing a cascading error effect.

Also, if you are going to be using ASyncStorage in a component file (again, would recommend putting this in a component/class where your data is accessed throughout the app...) and you are going to use it to decide the app should navigate forwards/backwards... definitely remove it from the render function and put it in maybe the constructor, componentWillReceiveProps, componentDidReceiveProps or componentWillUpdate lifecycle functions. That way it fires based on an update, a new passed prop obj or one time as the component is built. Anything is better than firing it every single render.

Lastly, I do not know what you have setup for your StackNavigator route stack object but you would need to have the keyword you used "DashboardScreen" in there pointing to an actual component that has been imported properly. The "DashboardScreen" keyword most likely would connect in your StackNavigator object to some component import like so...

import Dashboard from '../Views/DashboardScreenView';

StackNavigator({
  DashboardScreen: {
    screen: Dashboard,
    path: 'dashboard/:main',
    navigationOptions: null,
  },
});
GoreDefex
  • 1,461
  • 2
  • 17
  • 41
  • Thanks. Suppose I have one component `EnableNotification` from which I am calling `navigate("CreateMessage")` to open `CreateMessage` component. It is starting a component but not finishing `EnableNotification` component. This is what I am looking. Whats way to destroy EnableNotification – N Sharma Aug 23 '17 at 07:04
  • BTW Your suggestions are really appreciable. I don't know how to declare `AsyncStorage` in other js file and use it in my component ? can you add some snippet line of code ? – N Sharma Aug 23 '17 at 07:05
  • sorry. i think it is the question which actually misleading. it should be specified as "in AsyncStorage callback". but perhaps it's not the issue he had. i don't know whether it is better to put your answer as an answer or as a comment. because your answer is focusing on "within AsyncStorage callback" scope – stackunderflow Aug 30 '18 at 09:58
  • the real answer is to use navigation.dispatch. it's confusing why you didn't mention it. i think it's good to consider this is an old issue – stackunderflow Aug 30 '18 at 09:59
  • because I was not familiar with the package this person was using and could only offer obvious observations in which 4 other people found useful enough to upvote. It is not the selected answer. – GoreDefex Aug 30 '18 at 12:02
4

There is a simple way here: use "replace" (reference link repleace in navigation ,For example, you are at the screen "Login" , and you want to move to screen "Home", insert this code in screen "Login"

                         <TouchableOpacity onPress={() => { this.login() }}>

                            <Text}>Click me to Login</Text>

                        </TouchableOpacity>

and method login:

login(){
  this.props.navigation.replace('Home')
 }

Screen "Login" will be replaced by "Home", in Android, press Back Button =>app exit, no back screen "Login"

truong luu
  • 392
  • 1
  • 14
3

Based on your requirement, i suggest following setup:

SplashNavigator.js

const SplashNavigator = StackNavigator({
  Splash: {
    screen: SplashScreen,
    navigationOptions: {
      header: {
        visible: false
      }
    }
  }
});

AppNavigator.js

const AppNavigator = StackNavigator(
  {
    EnableNotification: {
      screen: EnableNotificationScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    CreateMessage: {
      screen: CreateMessageScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    },
    Dashboard: {
      screen: DashboardScreen,
      navigationOptions: {
        header: {
          visible: false
        }
      }
    }
  },
  {
    initialRouteName: "EnableNotification"
  }
);

In your index.android.js, you will render the SplashNavigator.

The SplashNavigator will render the SplashScreen. It has initial state value isReady set to false, so it will render a loading text until the @ProductTour:key value from AsyncStorage is loaded (AsyncStorage is async function, u should not put it in your render function). It will then render your AppNavigator and render your EnableNotification as initial route.

class SplashScreen extends Component {
  constructor() {
    super(props);
    this.state = {
      isReady: false,
    }
  }

  componentDidMount() {
    AsyncStorage.getItem("@ProductTour:key").then(value => {
      console.log(value);
      // you will need to handle case when `@ProductTour:key` is not exists
      this.setState({
        isReady: true,
      });
    });
  }

  render() {
    const { isReady } = this.state;
    return (
      <View style={{flex: 1}}>
        {
          isReady ?
          <AppNavigator />
          : <Text>Loading</Text>
        }
      </View>
    );
  }
}

Then on EnableNotificationScreen and CreateMessageScreen, change your navigate route function to use NavigationActions.reset from doc

Example:

import { NavigationActions } from 'react-navigation';

handleOnPressButton = () => {
  const resetAction = NavigationActions.reset({
    index: 0,
    actions: [
      NavigationActions.navigate({ routeName: "CreateMessage" })
    ]
  });
  this.props.navigation.dispatch(resetAction);
}
alpha
  • 1,103
  • 7
  • 10
  • Thanks @alpha. I think some points are still missing. Suppose I have one component `EnableNotification` from which I am calling `navigate("CreateMessage")` to open `CreateMessage` component. It is starting a component but not finishing `EnableNotification` component. This is what I am looking. Whats way to destroy `EnableNotification` – N Sharma Aug 23 '17 at 07:01
  • @Williams I don't quite get your question, can you tell us what is your navigation flow and how user navigate to `EnableNotification` screen. – alpha Aug 23 '17 at 07:16
  • It starts with `SplashScreen` -> `EnableNotification` -> `CreateMessage` -> `DashboardScreen`. I have button on each component and on clicking on it I am calling `navigate(name_of_the_component`. It works but components are not being destroyed. – N Sharma Aug 23 '17 at 07:20
  • @Williams So what you want is like step wizard, user cannot navigate back after user navigated to `DashboardScreen`, am I correct? – alpha Aug 23 '17 at 07:25
  • Nope. When I am navigating from `SplashScreen` to `EnableNotification` then `SplashScreen` should be destroyed, if I am navigating from `EnableNotification` to `CreateMessage` then `EnableNotification` should be destroyed and if I am navigating from `CreateMessage` to `DashboardScreen` then `CreateMessage` should be destroyed. As of now no component is being destroyed – N Sharma Aug 23 '17 at 07:32
  • @Williams Updated my answer based from your comments – alpha Aug 23 '17 at 08:25
  • Thanks. Do you mean that https://reactnavigation.org/docs/navigators/navigation-actions#Reset is for destroying component ? right ? – N Sharma Aug 23 '17 at 09:32
  • It is for reset navigation stack. For example if you use `navigation.navigate`, it will push a new stack to your existing stack(this will allow user to back previous screen), but if you use `NavigationActions.reset`, it will replace the stack(by index number of the routes) with action that you replace it(`navigate` in this case) – alpha Aug 23 '17 at 09:43
  • Whats for pop component ? Is replace kinda pop ? – N Sharma Aug 23 '17 at 11:20
  • to pop a stack, u can use `goBack` from https://reactnavigation.org/docs/navigators/navigation-prop#goBack-Close-the-active-screen-and-move-back, this will unmount current stack and back to previous stack – alpha Aug 23 '17 at 11:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/152651/discussion-between-williams-and-alpha). – N Sharma Aug 23 '17 at 15:10
  • I see that it reset and goback is for different purpose. The Reset action wipes the whole navigation state and replaces it with the result of several actions. and goback destroy current screen and sent on previous screen. My requirement is to destroy current screen and send to next screen. – N Sharma Aug 23 '17 at 15:20
  • @alpha you didn't understand the question yet you confidentally answer it like it's your birth right. thank you for wasting my time and many more by mislead us to you – stackunderflow Feb 14 '18 at 23:13
0

Yes in react native you can finish the current screen before navigating to new screen with the help of NavigationActions . Please refer this link -

http://androidseekho.com/others/reactnative/finish-current-screen-on-navigating-another-in-react-native/

priyanka
  • 171
  • 2
  • 12
0

SplashNavigator.js

const SplashNavigator = StackNavigator({
  Splash: {
    screen: SplashScreen,
    navigationOptions: {
      header: null}
    }
  }
});
Matt
  • 1,518
  • 4
  • 16
  • 30
-1

Import StackActions and NavigationActions from react-navigation.

import { StackActions, NavigationActions } from 'react-navigation';

below code for performing Action

navigateToHomeScreen = () => {
  const navigateAction = StackActions.reset({
    index: 0,
    actions: [NavigationActions.navigate({ routeName: "HomeScreen" })],
  });

  this.props.navigation.dispatch(navigateAction);
}
SWAPDROiD
  • 357
  • 3
  • 15