Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combining Router and navigation Drawer #92

Closed
avishayil opened this issue Dec 25, 2015 · 28 comments
Closed

Combining Router and navigation Drawer #92

avishayil opened this issue Dec 25, 2015 · 28 comments

Comments

@avishayil
Copy link

Hi!
I'm trying to create a navigation drawer that will point his text elements to jump between screens.
For the matter, I'm using react-native-drawer by root-two.
Do you have any example or can give me some direction of how to combine those two? I can't seem to figure that out.
Thanks!

@aksonov
Copy link
Owner

aksonov commented Jan 4, 2016

It is interesting question, have you tried set Drawer as 'footer' prop for Router?

@JonnyBGod
Copy link

+1

@BigPun86
Copy link

Any progress on this? I am trying to use react-native-side-menu module with this router-flux. Actually it only works until i try to call any Actions.

My SideMenu combined with router-flux:

 <SideMenu
     menu={<Menu/>}
     isOpen={false} >
        <Router hideNavBar={true}>
              <Schema name="modal" sceneConfig={Navigator.SceneConfigs.FloatFromBottom}/>
              <Schema name="default" sceneConfig={Navigator.SceneConfigs.FloatFromBottomAndroid}
                        titleStyle={{color: 'white'}}
                        backButtonStyle={{tintColor: 'white'}}
                        navigationBarStyle={{backgroundColor: Colors.MAIN}}
                        barButtonText={{color: 'white'}}  />
              <Schema name="searchbar" sceneConfig={Navigator.SceneConfigs.FloatFromRight}
                        titleStyle={{color: 'white'}} backButtonStyle={{tintColor: 'white'}}
                        navigationBarStyle={{backgroundColor: Colors.MAIN}}/>
              <Route name="launch" component={Launch}/>
              <Route name="dashboard">
              <Router hideNavBar={false}>
                 <Route name='home' component={Dashboard} title={Strings('home')}         type='replace'
                               renderTitle={renderTitle}/>

       ..........
       ..........

It works up to the point when i try calling any actions from the Side Menu, where unfortunately nothing happens.

A Button in my Side Menu, which should call Home Screen:

 <Button onPress={Actions.home}>
        <View style={styles.row}>
             <Image style={styles.itemImage} source={require('../Assets/SideMenu/home_off.png')}/>
             <Text style={styles.itemText}>Home</Text>
        </View>
  </Button>

Any ideas why Actions won´t work?

@aksonov
Copy link
Owner

aksonov commented Jan 14, 2016

Try {()=>Actions.home()} because Actions.home may not defined during compile time...

@BigPun86
Copy link

Okay nice, it fixed it thanks. Now when i try to put my Side Menu over a nested Router, something like:

<Router hideNavBar={true}>
     <Schema ......
     <Route name="launch" component={Launch}/>
     <Route name="dashboard" component={Dashboard}/>
         <SideMenu
             menu.....>
                   <Router hideNavBar={false}>
                        <Route name='home' component={Dashboard} title={Strings('home')} type='replace'
                               renderTitle={renderTitle}/>
                        <Route ...../>
                        <Route ...../>
                        <Route ...../>
                    </Router>
           </SideMenu>
      <Route ...../>
      <Route ...../>
</Router>

It throws me this error when i try to call an Action:

screenshot_2016-01-14-13-46-03

And the NavigationBar loses it´s Styling...

@aksonov
Copy link
Owner

aksonov commented Jan 15, 2016

SideMenu should pass its 'router' prop to his children, otherwise router hierarchy would be broken.

@BigPun86
Copy link

Worked, thanks!

@rturk
Copy link
Collaborator

rturk commented Jan 20, 2016

Hi @BigPun86 can you share your code? I'm trying to build similar logic with a nested tabbar. @sibeliusseraphini

@JonnyBGod
Copy link

An example would be helpful.

@artem-russkikh
Copy link

+1

@GarrettSmith
Copy link

It took me awhile but I got to the bottom of this.

My top level render looks like this.

<Router>
  <Route name="without-drawer"/>
  <Route name="main">
   <Drawer>
      <Router>
        <Route name="with-drawer-a"/>
        <Route name="with-drawer-b"/>
      </Router>
    </Drawer>
  </Route>
</Router>

My drawer's render function looks like this.

<DrawerLayout>
   {React.Children.map(children, c => React.cloneElement(c, {route: this.props.route}))}
</DrawerLayout>

@BigPun86
Copy link

BigPun86 commented Feb 6, 2016

Hey there. This is how i use the Drawer/SideMenu. In my Main Router i setup as usual my routes. Then i have a nested router Dashboard where i need the Drawer/SideMenu

        <Router hideNavBar={true}>
            <Schema name="modal" sceneConfig=......................./>
            <Schema name="default" sceneConfig=.............../>
            <Schema name="searchbar" sceneConfig=.............../>

            <Route name="dashboard" schema="dashboard">
                <Router hideNavBar={false}>
                    <Route name='home' component={ Routes.Dashboard }  title={translate('dashboard')} type='reset'
                           renderTitle={renderTitle}/>
                    <Route name="chatAndGroups" component={ Routes.ChatAndGroups } title={translate('myGroups')}
                           renderTitle={renderTitle} renderLeftButton={renderBackButton}/>
                    <Route name="reviews" component={ Routes.Reviews} title={translate('reviews')}
                           renderTitle={renderTitle}
                           renderLeftButton={renderBackButton}/>
                    <Route name="weather" component={ Routes.Weather} title={translate('weather')}
                           renderTitle={renderTitle}
                           renderLeftButton={renderBackButton}/>
                </Router>
            </Route>
            <Route name="launch" component={Routes.Launch} initial={true}/>
            <Route name="tutorial" component={Routes.Tutorial}/>

            <Route name="disclaimer" component={ Routes.Disclaimer} title={translate('disclaimer')}
                   renderTitle={renderTitle}
                   renderLeftButton={renderBackButton}/>
            <Route name="login" component={ Routes.Login} title={translate('login')} showNavigationBar={false}
                   renderLeftButton={renderBackButton}/>
            <Route name="register" component={ Routes.Register} title={translate('createAccount')}
                   renderLeftButton={renderBackButton}/>
        </Router>
    )
}

In my Dashboard component i implemented the SideMenu:

export default class Dashboard extends Component {
constructor(props) {
    super(props);
}

render() {

    return (
        <SideMenu
            menu={<Menu />}
            isOpen={false}
        >
            <ContentView/>
        </SideMenu>
    )
}
}

@oviava
Copy link

oviava commented Feb 8, 2016

@BigPun86 do you have a repo for this ? How does the navigator know to render your scene in ?!

@mmazzarolo
Copy link
Contributor

Thank you @BigPun86, your solution is working for me.
If anyone needs an example I used the sidebar in this common pattern:

  • A session screen that just checks if the user is already logged in on startup
  • An auth screen where I manage signin/signup
  • Many other screens where that show the true content of the app, all using the sidebar

Router.js

export default class Routes extends React.Component {
  render() {
    return (
      <Router>
        <Route component={SessionScreen} name='session' type='replace' hideNavBar={true} initial={true} />
        <Route component={AuthScreen} name='auth' type='replace' />
        <Route name='main' hideNavBar={true} type='reset'>
          <SideDrawer>
            <Router>
              <Route component={PlaceScreen} name='place' title='Place' />
              <Route component={ItemScreen} name='item' title='Item' />
              ...
            </Router>
          </SideDrawer>
        </Route>
      </Router>
    )
  }
}

SideDrawer.js (I'm using 'react-native-drawer')

import Drawer from 'react-native-drawer'

class SideDrawer extends React.Component {
  render() {
    return (
      <Drawer
        type="overlay"
        content={<SideDrawerContent />}
        tapToClose={true}
        openDrawerOffset={0.2} 
        panCloseMask={0.2}
        closedDrawerOffset={-3}
        styles={{ drawer: drawerStyle, main: mainStyle }}
        tweenHandler={(ratio) => ({ main: { opacity: (2 - ratio) / 2 } })}
      >
        {React.Children.map(this.props.children, c => React.cloneElement(c, {
          route: this.props.route
        }))}
      </Drawer>
    )
  }
}

Be careful of #172 that you can fix by adding in AuthScreen.js:

componentWillUnmount() {
  // Fix https://github.com/aksonov/react-native-router-flux/issues/172
  Actions.currentRouter = null
}

@BigPun86
Copy link

@mmazzarolo nice example. I tried yours which works for me too even better than mine. But how do you pass the schema to the children? I tried this:

{React.Children.map(this.props.children, c => React.cloneElement(c, {
      route: this.props.route,
      schema: this.props.schema
    }))}

This is what worked to pass the navigationBarStyle, but i am not sure if passing the schema would be nicer...:

{React.Children.map(this.props.children, c => React.cloneElement(c, {
      route: this.props.route,
      navigationBarStyle: this.props.navigationBarStyle
    }))}

@mmazzarolo
Copy link
Contributor

Thank you @BigPun86!
You don't need to pass the schema/styles to the children:
Router.js

const hideNavBar = Platform.OS === 'android'
const paddingTop = Platform.OS === 'android' ? 0 : 8

export default class Routes extends React.Component {
  render() {
    return (
      <Router>
        <Schema
          name='boot'
          sceneConfig={Navigator.SceneConfigs.FadeAndroid}
          hideNavBar={true}
          type='replace'
        />
        <Schema
          name='main'
          sceneConfig={Navigator.SceneConfigs.FadeAndroid}
          hideNavBar={hideNavBar}
        />

        <Route schema='boot' component={SessionScreen} name='session' initial={true} />
        <Route schema='boot' component={AuthScreen} name='auth' />

        <Route name='main' hideNavBar={true} type='reset'>
          <SideDrawer>
            <Router
              sceneStyle={styles.scene}
              navigationBarStyle={styles.navigationBar}
              titleStyle={styles.title}
              barButtonIconStyle={styles.barButtonIcon}
              barButtonTextStyle={styles.barButtonText}
            >
              <Route schema='main' component={PlaceScreen} name='place' title='Places' />
              <Route schema='main' component={PaymentScreen} name='payment' title='Payment' header={Toolbar} />
            </Router>
          </SideDrawer>
        </Route>

      </Router>
    )
  }
}

The router above is working for me, hope it helps.

@zebulgar
Copy link
Contributor

thanks @mmazzarolo for the well-structured code. That ended up working for me better than my hackish implementation of the drawer!!!

@aksonov
Copy link
Owner

aksonov commented Feb 10, 2016

@mmazzarolo Good work! Could you please add this as section to README?

@zebulgar
Copy link
Contributor

hey @aksonov got a quick moment to hop onto Gitter?

@mmazzarolo
Copy link
Contributor

Thanks @MITDelian, but I just stolen and implemented the idea from @GarrettSmith's comment.
@aksonov can you take a look at #69 ? I can be helpful with the README/wiki if you want!

@blocka
Copy link

blocka commented Feb 28, 2016

@mmazzarolo
I've tried setting up your example. When I call Actions.main (the route with the drawer) I get undefined is not a function this._navBar.immediatelyRefresh()

@davidfloegel
Copy link

Sorry for bringing this up again!

If I wrap a Router in the I can't seem to access the context anymore and therefore not open/close the sidebar.

Any ideas? @mmazzarolo

@BigPun86
Copy link

BigPun86 commented May 14, 2016

Not sure what u mean exactly...But this is how i implemented my Router with SideMenu (react-native-drawer)

.............................
<SideDrawer ref="drawer" router={this.refs.router}>
                <Router
                    ref="router"
                    firstRoute={{   
.......................

export default class SideDrawer extends Component {

constructor(props) {
    super(props);
}

render() {
    var userToken = store.getState().User.token;
    return (
        <Drawer
            ref="drawer"
            type="overlay"
            content={<Menu  {...this.props} />}
            tapToClose={true}
            openDrawerOffset={0.2}
            panCloseMask={0.2}
            closedDrawerOffset={0}
            tweenHandler={(ratio) => ({ main: { opacity: (2 - ratio) / 2 } })}
            disabled={ userToken === '' || userToken === null || userToken === undefined }
        >
            {React.Children.map(this.props.children, c => React.cloneElement(c, {
                route: this.props.route
            }))}
        </Drawer>
    )
}

}

The main thing is to give your router and sidedrawer a ref and you can than acces the trigger for opening closing from anywhere like this:

this.refs.drawer.refs.drawer.open();

@davidfloegel
Copy link

davidfloegel commented May 15, 2016

Hmm yeah that is strange you see..

My Main Component:

class App extends Component {
    render() {
        return (
            <Router key='mainrouter' name='router' sceneStyle={ styles.router }>
                <SideDrawer key='drawer' ref='drawer' router={ this.refs.router }>
                    <Router ref='router' key='root' duration={ 100 }>
                        <Scene key='home' initial={ true } component={ Home } hideNavBar={ true } />
                    </Router>
                </SideDrawer>
            </Router>
        );
    }
}

My SideDrawer:

export default class SideDrawer extends Component {
  render () {
    return (
      <Drawer
        ref='drawer'
        type={'overlay'}
        content={<ControlPanel />}
        openDrawerOffset={ (viewport) => viewport.width - 250 }
        tweenDuration={ 100 }
        tapToClose={ true } >
        {React.Children.map(this.props.children, c => React.cloneElement(c, {
          route: this.props.route
        }))}
      </Drawer>
    )
  }
};

And my 'home' component where I try to open and close the drawer:

render() {
   return (
        <TouchableOpacity onPress={ () => this.refs.drawer.open() }>
              <Text>Open Drawer</Text>
        </TouchableOpacity>
   )
}

But if I click the TouchableOpacity I get a cannot read property open of undefined.
(I have tried this.res.drawer.refs.drawer.open as well - but I thought it was a typo?)

Any ideas?

@BigPun86
Copy link

BigPun86 commented May 15, 2016

@davidfloegel do you have Discord? So we could solve it there....

@davidfloegel
Copy link

Just got an account! davidfloegel is my username!

@rajeshkos
Copy link

Hi,

I have used react-native-router-flux and react-native-drawer together. Can anybody help me to show drawer only for dashboard screen not for login.

@pedroenriquefreitas
Copy link

pedroenriquefreitas commented Sep 7, 2017

There is some way to use react-native-router-flux and react-native-side-menu together, using the latest version of both? Seems like all these answers are outdated.

This is my code. It works at the main Scene, but as you go to the others it everything goes blank (the side bar actually still works at that part).

const RouterComponent = () => {

  return (
    <SideMenu menu={<Menu navigator={navigator}/>}>
      <Router>
        <Stack key="teams">
            <Scene key="league_list" component={LeagueList} title="Leagues" initial />
            <Scene key="team_list" component={TeamList} title="Teams" />
            <Scene key="soloteam" component={Team} />
        </Stack>
      </Router>
    </SideMenu>
  );
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests