wix / react-native-navigation

A complete native navigation solution for React Native
https://wix.github.io/react-native-navigation/
MIT License
13.04k stars 2.67k forks source link

How to get the current screen? #75

Closed musicode closed 7 years ago

musicode commented 8 years ago

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 ?

guyca commented 8 years ago

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

jarredwitt commented 8 years ago

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.

musicode commented 8 years ago

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.

DanielZlotin commented 8 years ago

@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.

finnfiddle commented 8 years ago

+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..?

musicode commented 8 years ago

When to support this feature?

musicode commented 8 years ago

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

DanielZlotin commented 8 years ago

@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

musicode commented 8 years ago

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

many many thanks to you.

musicode commented 7 years ago

A month has passed..

adamski commented 7 years ago

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

DanielZlotin commented 7 years ago

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

musicode commented 7 years ago

hi, any updates on this?

guyca commented 7 years ago

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

musicode commented 7 years ago

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.

musicode commented 7 years ago

@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!

jameskhamil commented 7 years ago

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?

annelorraineuy commented 7 years ago

@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).

ghost commented 4 years ago

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.

rbecharast-chwy commented 4 years ago

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

ghost commented 4 years ago

@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.

rbecharast-chwy commented 4 years ago

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

ghost commented 4 years ago

@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 😉

rbecharast-chwy commented 4 years ago

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

ghost commented 4 years ago

After trying let us know if it worked...

rbecharast-chwy commented 4 years ago

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

IlliaFilatov commented 4 years ago

@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.

samuelseaton commented 3 years ago

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;
}
JamesLarson commented 3 years ago

Great workarounds, but is there any chance we'll see something like Navigation.getCurrentScreen()?

guyca commented 3 years ago

@JamesLarson Yes. Once RNN switches to a sync API with JSI we will probably expose this method. But as long as our API is async, we're refraining from introducing this API as it will result in poor performance and design on your end.

ckastli commented 2 years ago

The hook proposed by @samuelseaton is exactly what I was looking for. It will be nice to see those hooks on react-native-navigation-hooks, don't you think it so @underscopeio?

useNavigationComponentId

import { useContext } from 'react'
import { NavigationContext } from 'react-native-navigation-hooks'

const useNavigationComponentId = () => {
  const { componentId } = useContext(NavigationContext)

  return componentId
}

export default useNavigationComponentId

useNavigationCurrentComponentId

import { useEffect, useState } from 'react'
import { Navigation } from 'react-native-navigation'
import useNavigationComponentId from './useNavigationComponentId'

const useNavigationCurrentComponentId = () => {
  const componentId = useNavigationComponentId()

  const [currentComponentId, setCurrentComponentId] = useState(componentId)

  useEffect(() => {
    const subscription = Navigation.events().registerComponentDidAppearListener(event => {
      setCurrentComponentId(event.componentId)
    })

    return () => {
      subscription.remove()
    }
  }, [])

  return currentComponentId
}

export default useNavigationCurrentComponentId