react-navigation / redux-helpers

Redux middleware and utils for React Navigation
Other
296 stars 43 forks source link

I don't know how to handle the Hardware Back Button in Android in new API.(2.0.2). Help me please, thanks #44

Closed DengZhouLi closed 6 years ago

Ashoat commented 6 years ago

https://reactnavigation.org/docs/en/redux-integration.html#handling-the-hardware-back-button-in-android

jhesgodi commented 6 years ago

@Ashoat Thank you for putting this new version. I would like to get some more context on the required setup for handling the back button integration.

From following the redux-integration docs, I don't clearly get how to connect the HOC to be able to handle it like this:

    /* more setup code here! this is not a runnable snippet */ 
    return <AppNavigator navigation={navigation} />;

So I choose to connect separately the AppNavigator using the reduxifyNavigator. This way it works but I'm not certain if this would be the best approach. I would like to understand how to pass navigation as prop as shown on the docs.

This is what i'm currently doing:

// Setup navigator
const MainStack = createStackNavigator(routesConfig);
const Drawer = createDrawerNavigator(
  {
    Main: { screen: MainStack },
  },
  {
    contentComponent: NavigationDrawer,
  },
);

export const AppNavigator = createStackNavigator(
  {
    Splash: {
      screen: SplashScreen,
      description: 'Loading screen',
    },
    Drawer: {
      screen: Drawer,
      description: 'main drawer',
    },
  },
  {
    initialRouteName: 'Splash',
    headerMode: 'none',
  },
);

// Create middleware and connect
export const appNavigatorMiddleware = createReactNavigationReduxMiddleware(
  'root',
  state => state.nav,
);

const mapNavStateProps = state => ({
  state: state.nav,
});
const reduxAppNavigator = reduxifyNavigator(AppNavigator, 'root');
const AppNavigatorWithState = connect(mapNavStateProps)(reduxAppNavigator);

// create nav component
export default class ReduxNavigation extends PureComponent {
  componentDidMount() {
    BackHandler.addEventListener("hardwareBackPress", this.onBackPress);
  }

  componentWillUnmount() {
    BackHandler.removeEventListener("hardwareBackPress", this.onBackPress);
  }

  onBackPress = () => {
    const { dispatch, nav } = this.props;
    if (nav.index === 0) {
      return false;
    }

    dispatch(NavigationActions.back());
    return true;
  };

  render() {
    return <AppNavigatorWithState />;
  }
}

Thanks in advance

Ashoat commented 6 years ago

Looks like the Redux integration docs need to be updated. If anybody can do a PR on this file to update them, it would be much appreciated.

The long and short of it is that reduxNavigator handles the navigation property mentioned in the docs. You pass it dispatch and state, and it converts those to navigation and passes it to your root navigator component (the one you passed to reduxNavigator).

@jhesgodi: The only thing I would change in your code is that instead of calling connect twice - once on ReduxNavigation and once on reduxAppNavigator - I would call it just once, on ReduxNavigation, and pass the dispatch and state props in from ReduxNavigation directly to reduxAppNavigator. If you fix that up you'll have the right code to update the docs to!

jhesgodi commented 6 years ago

Thanks for the help @Ashoat.

For anyone else looking for clues, this was my final working output

// Setup navigator
const MainStack = createStackNavigator(routesConfig);
const Drawer = createDrawerNavigator(
  {
    Main: { screen: MainStack },
  },
  {
    contentComponent: NavigationDrawer,
  },
);

export const MainNavigator = createStackNavigator(
  {
    Splash: {
      screen: SplashScreen,
      description: 'Loading screen',
    },
    Drawer: {
      screen: Drawer,
      description: 'Main drawer',
    },
  },
  {
    initialRouteName: 'Splash',
    headerMode: 'none',
  },
);

// Create middleware and connect
export const appNavigatorMiddleware = createReactNavigationReduxMiddleware(
  'root',
  state => state.nav,
);

const AppNavigator = reduxifyNavigator(MainNavigator, 'root');

// create nav component
class ReduxNavigation extends PureComponent {
  componentDidMount() {
    BackHandler.addEventListener("hardwareBackPress", this.onBackPress);
  }

  componentWillUnmount() {
    BackHandler.removeEventListener("hardwareBackPress", this.onBackPress);
  }

  onBackPress = () => {
    const { navigation, dispatch } = this.props;
    if (navigation.index === 0) {
      return false;
    }

    dispatch(NavigationActions.back());
    return true;
  };

  render() {
   const { navigation, dispatch, width, height } = this.props
   const screenProps = { width, height, /*...*/ }

    return <AppNavigator
        state={navigation}
        dispatch={dispatch}
        screenProps={screenProps} // optional
     />;
  }
}

const mapStateToProps = state => ({
  navigation: state.nav,
 // other optional props
  width: state.system.layout.width,
  height: state.system.layout.height - state.system.metrics.statusBarHeight,
});

export default connect(mapStateToProps)(ReduxNavigation);
Psiiirus commented 5 years ago

You should really put this into the README :)

Ashoat commented 5 years ago

PRs welcome! We have a little pseudocode snippet for the back button at the bottom of the README, but it could always be improved. The snippet above is probably a bit more verbose than we need, but definitely open to improvements.