How can I tell if the screen is navigated to with

2020-07-10 07:03发布

问题:

I'd like to refresh the data on the screen in a react native app whenever the screen appears - like in a ViewWillAppear method. I tried using the componentWillMount method but it looks like it fires once before it appears, and doesn't fire again when the view is navigated to again.

Looking at this example https://reactnavigation.org/docs/guides/screen-tracking, it looks like I can add a listener on the onNavigationStateChange method on the root navigation, but I'd like to keep the logic in the screen as it gets confusing if I move the data fetch logic for that one scree outside to the root navigator.

I've tried to follow the example and set that method to my stacknavigation but it doesn't seem to trigger.

  <RootNavigation ref={nav => { this.navigator = nav; }}
    onNavigationStateChange={(prevState, currentState, action) => {

      // maybe here I can fire redux event or something to let screens
      // know that they are getting focus - however it doesn't fire so
      // I might be implementing this incorrectly

      const currentScreen = getCurrentRouteName(currentState);
      const prevScreen = getCurrentRouteName(prevState);

      if (prevScreen !== currentScreen) {
        console.log('navigating to this screen', currentScreen);
      }
    }}
  />

回答1:

So here's how I did it using the onNavigateStateChange.

      <RootNavigation
        ref={nav => { this.navigator = nav; }}
        uriPrefix={prefix}
        onNavigationStateChange={(prevState, currentState) => {
          const currentScreen = this.getCurrentRouteName(currentState);
          const prevScreen = this.getCurrentRouteName(prevState);
          if (prevScreen !== currentScreen) {
            this.props.emitActiveScreen(currentScreen);
            {/*console.log('onNavigationStateChange', currentScreen);*/}
          }
        }}
      />

And in your screen you can check to see if your view will appear, note MyPage is the route name from your navigation object.

  componentWillReceiveProps(nextProps) {
    if ((nextProps.activeScreen === 'MyPage' && nextProps.activeScreen !== this.props.activeScreen)) {
      // do your on view will appear logic here
    }
  }


回答2:

Here is my navigator reducer.

function getCurrentRouteName(navState) {
  if (!navState) {
    return null;
  }

  const navigationState = (navState && navState.toJS && navState.toJS()) || navState;

  const route = navigationState.routes[navigationState.index];
  // dive into nested navigators
  if (route.routes) {
    return getCurrentRouteName(route);
  }

  return route.routeName;
}


export default function NavigatorReducer(state, action) {
  // Initial state
  if (!state) {
    return fromJS(AppNavigator.router.getStateForAction(action, state));
  }

  // Is this a navigation action that we should act upon?
  if (includes(NavigationActions, action.type)) {
    // lets find currentScreen before this action based on state
    const currentScreen = getCurrentRouteName(state);
    const nextState = AppNavigator.router.getStateForAction(action, state.toJS());
    // determine what the new screen will be after this action was performed
    const nextScreen = getCurrentRouteName(nextState);

    if (nextScreen !== currentScreen) {
      nextState.currentRoute = nextScreen;
      console.log(`screen changed, punk: ${currentScreen} -> ${nextScreen}`);
    }

    return fromJS(nextState);
  }

  return state;
}

And then we have to connect the module/route to the redux store (sceneIsActive is the important bit):

export default connect(
  state => ({
    counter: state.getIn(['counter', 'value']),
    loading: state.getIn(['counter', 'loading']),
    sceneIsActive: state.getIn(['navigatorState', 'currentRoute']) === 'Counter',
  }),
  dispatch => {
    return {
      navigate: bindActionCreators(NavigationActions.navigate, dispatch),
      counterStateActions: bindActionCreators(CounterStateActions, dispatch),
    };
  },
)(CounterView);

And then inside your component, you can watch for/trigger code when the scene becomes active:

  componentWillReceiveProps(nextProps) {
    if (nextProps.sceneIsActive && (this.props.sceneIsActive !== nextProps.sceneIsActive)) {
      console.log('counter view is now active, do something about it', this.props.sceneIsActive, nextProps.sceneIsActive);
      doSomethingWhenScreenBecomesActive();
    }
  }

Know that componentWillReceiveProps does not run when initially mounted. So don't forget to call your doSomethingWhenScreenBecomesActive there as well.



回答3:

You can use focus/blur event listeners(demonstrated here and discussed here).