React-native-navigation: How to get the current screen?

Created on 23 Jun 2016  ·  28Comments  ·  Source: wix/react-native-navigation

i call navigator.push() to push a screen, then i press back button.

if i want to get the current screen, i thick there are two ways:

  1. trigger some events, such as push and pop
  2. the api provides the Navigation.getCurrentScreen()

i think the first way is better, but now, how can i do it ?

All 28 comments

@musicode This functionality is indeed missing, we'll add it soon.

Took a stab at this because I needed to know if a drawer was toggled for hiding the status bar. Still pretty new to this library and my Objective-C sucks, so bear with me.

I decided that adding a listener to the NativeAppEventEmitter(only tested this on iOS) was probably my best route to make sure that I didn't introduce any breaking changes. So here we go.

After my store has been initialized I add an event listener:

NativeAppEventEmitter.addListener('ReactNativeNavigationEvent', (event) => {
    console.log(event);
    // We get access to everything that is passed when performing something like
    // this.props.navigator.push({...}); The only addition is that there is a type that is added
    // in the form of NavigationEvent_{performAction} eg: NavigationEvent_push.
});

With the code above if you're using Redux you can dispatch an action to a reducer that is set up to handle those types of actions and take what you want from the event object such as the component string and update your state.

In the ReactNativeControllers iOS project I created a helper called RCCNavigationEventNotifier with a static method sendNavigationEvent.

+ (void)sendNavigationEvent:(NSString*)actionType actionParams:(NSDictionary*)actionParams
{
    [[[RCCManager sharedInstance] getBridge].eventDispatcher sendAppEventWithName:@"ReactNativeNavigationEvent" body:@
     {
         @"type": [NSString stringWithFormat:@"NavigationEvent_%@", actionType],
         @"params": actionParams
     }];
}

Pretty much just copied what the buttons use to send events to listeners. After importing the header file to RCCNavigationController.m I added the following to the performAction:

  // Array of nav actions that can trigger a navigation event.
  NSArray *navEventActions = @[@"push", @"pop", @"popToRoot", @"resetTo"];
  if ([navEventActions containsObject:performAction]) {
    [RCCNavigationEventNotifier sendNavigationEvent:performAction actionParams:actionParams];
  }

Pretty quick and dirty right now, but it works pretty well for my needs to be able to update my state with nav events. I know that you said that you'll add this soon, if this is the right direction I can certainly clean it up and make a pull request or trash it. It's all local right now so if you want to take a closer look I can make a fork as well.

how cant I get current screen in v2.0?

I didn't find the api. the feeling of v2.0 is the same as v1.0.

@musicode well, 2.0.0 isn't out yet. You are using the experimental pre-release.

The current state of the framework is that we try to create parity between the platforms, unify both controllers and navigation, and upgrade to RN31. After that we have a bunch of refactoring and redesign of the architecture to do. One of the requested features is indeed accessing a specific screen somehow. This will be addressed.

+1 for this. Not sure if this would be returned by getCurrentScreen but I am also in need of a method to get the current title and navigator buttons. I want to call navigator.setTitle() but first save the previous title so I can roll back to it later programmatically if need be. Or maybe there is another way to do this..?

When to support this feature?

hi, have any new messages? I have wait it for 4 months, it is so long...

@musicode I feel your pain, and truly appreciate the patience. Deciding on the new architecture is not easy but it is my current and complete focus. I think we will have something ready in the upcoming month

@DanielZlotin thanks, I hope RNN could release 2.0 in the upcoming month.

many many thanks to you.

A month has passed..

Hi guys, sorry to pester, just wondering if there are any updates on this, even suggestions no how it might look?

Thank you for your patience. We are working hard on the v2 api that will solve this, among other things.

hi, any updates on this?

@musicode Screen lifecycle events were added in .233
https://github.com/wix/react-native-navigation/wiki/Screen-API#screen-visibility

thanks!

I have a question, there are many images in the demo gif of README, I want to know which component is used for display the fullscreen image when user press the thumbnail.

the component should support zoom by double tap, moving with finger, and the most important is, we hope as smooth as possible.

@guyca What do you think of react-navigation, I found it yesterday, but it's more difficult than RNN.

I have read its document for one day, but I still did't know how to get started...

It's recommand by react-native, so it double me.

I want to use RNN, but I don't know whether it's really ready for production, I don't know ios and android, i'm so helpless when some bugs is arised.

for more, i need some native components like image and rich text editor, i hope RNN will not conflict with the other native component.

that is all my doubt, I hope you could give me a answer.

thank you!

There's still no way to getCurrentScreen, right?

Just to be clear,

  1. ios cant have tabs on top
  2. So you have to render a custom component as the navbar
  3. So the navbar needs all kinds of state information to render the buttons correctly
  4. But no way to get current screen without attaching events directly to every component
  5. And no way to get name of screen while in component unless component has same name by some convention

Is 2.0 usable?

@musicode I was just looking at that because of so many problems here. Did you ever investigate?

@musicode @jameskhamil try #1118 ? I actually used react-navigation first, and then switched to this package. I find this has better performance, but RN is JS based so debugging is easier. My biggest issue with RN was that at the time, there was no way to disable swipe gestures (to hide side drawer).

That still relevant, how do I get the current screen data?

Something like:

Navigation.getCurrentScreen()

I'm trying to find it out, but docs are not helping.

Any solution? I need to know what the current screen is visible.

@rodribech20 As a workaround I'm traking the componentDidAppear event and storing the last component in a variable that is in a singleton in my app, it is working fine.

Thanks @emilioheinz it is a feasible solution. The thing is I have many screens on my App.

@rodribech20 I know... I have too, the thing is that you can use the global event to track it. In my app I have around 30 screens and it is working fine.

My registerComponentDidAppearListener function that is in the first file loaded in the application:

Navigation.events().registerComponentDidAppearListener(data => {
    const { componentName } = data
    const { COMPONENT_DID_APPEAR_BLACK_LIST } = NAVIGATION_CONSTANTS

    if(!COMPONENT_DID_APPEAR_BLACK_LIST.includes(componentName)) {
      NavigationService.setCurrentScreenData(data)
    }
  })

I'm using a black list to do not save components that are registered and are not a screen from where I can push another screen, components like topBar custom buttons.

NavigationService is a singleton service where I wrap all the Navigation functions and store the last pushed screen data.

class _NavigationService {
  constructor() {
    this.currentScreen = {}
  }

  setCurrentScreenData(data = {}) {
    this.currentScreen = data
  }

  // Other functions like push, mergeOptions, pop etc
}

So in any place of my application I can access NavigationService.currentScreen and I have the currentScreen data.

I hope it helped you 😉

@emilioheinz Great! I'm going to try it if it works for me. Thanks!!!

After trying let us know if it worked...

@emilioheinz Yes it is working! It is a great workaround . Thank you very much!

@rodribech20 I know... I have too, the thing is that you can use the global event to track it. In my app I have around 30 screens and it is working fine.

My registerComponentDidAppearListener function that is in the first file loaded in the application:

Navigation.events().registerComponentDidAppearListener(data => {
    const { componentName } = data
    const { COMPONENT_DID_APPEAR_BLACK_LIST } = NAVIGATION_CONSTANTS

    if(!COMPONENT_DID_APPEAR_BLACK_LIST.includes(componentName)) {
      NavigationService.setCurrentScreenData(data)
    }
  })

I'm using a black list to do not save components that are registered and are not a screen from where I can push another screen, components like topBar custom buttons.

NavigationService is a singleton service where I wrap all the Navigation functions and store the last pushed screen data.

class _NavigationService {
  constructor() {
    this.currentScreen = {}
  }

  setCurrentScreenData(data = {}) {
    this.currentScreen = data
  }

  // Other functions like push, mergeOptions, pop etc
}

So in any place of my application I can access NavigationService.currentScreen and I have the currentScreen data.

I hope it helped you 😉

It worked for me as well, but sometimes it makes navigator work way too slow, because of checking after each screen changing. So I created a method inside my navigator controller, that I imported to my screens, and passed screen name as a props.

activeTabController(componentId) {
  const { activeBottomTab } = this.state;
  activeBottomTab !== componentId && this.setState({
    activeBottomTab: componentId
  })
}

I know that this is not a very beautiful solution, but it works for me really faster than:

Navigation.events().registerComponentDidAppearListener()

Anyway, thank you for helping.

I wrote a hook that seems to be working for this.

import { useEffect, useState } from 'react';
import { Navigation } from 'react-native-navigation';

/**
 * Gets the current navigation screen
 */
export function useGetCurrentScreen(): string {
    const [currentScreen, setCurrentScreen] = useState('');

    useEffect(() => {
        let isMounted = true; // note this flag denote mount status
        Navigation.events().registerComponentDidAppearListener(({ componentId }): void => {
            if (isMounted) setCurrentScreen(componentId);
        });
        return (): void => {
            isMounted = false;
        }; // use effect cleanup to set flag false, if unmounted
    });

    return currentScreen;
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

viper4595 picture viper4595  ·  3Comments

zagoa picture zagoa  ·  3Comments

birkir picture birkir  ·  3Comments

edcs picture edcs  ·  3Comments

charlesluo2014 picture charlesluo2014  ·  3Comments