- so I don’t forget
- in case you’re trying to do the same (judging by the number of questions on StackOverflow it seems to be a common problem).
- if I’d known all this when I started I could have saved a few refactor loops.
The layout
Recording the drawer opening and closing
@connect(store => { return { menu: store.menu }; })
And a toggle event is handled by dispatching an action creator that stores the latest drawer state.
// toggle the drawer handleToggle = () => { this.props.dispatch (acDrawerOpen (!this.props.menu.drawerOpen)); };
The action creator
export function acDrawerOpen (term = null) { return { type: cs.actions.M_DRAWER_OPEN, payload: term }; }
The reducer
case cs.actions.M_DRAWER_OPEN: { return {...state,drawerOpen:action.payload}; }
Figuring out the width of the content
// adjust for left drawer and top, with a standard padding // for non fixed elements. const pad = 16; const appbarHeight = 64; const drawerWidth = 255; // this will be padding (or fixed positions) depending on whethere drawer is open const left = this.props.menu.drawerOpen ? drawerWidth : 0; const top = appbarHeight; const width = this.props.menu.drawerOpen ? 'calc(100% - ' + (drawerWidth+2*pad) + 'px)' : 'calc(100% - ' + (2*pad) + 'px)'; const contentWidth = this.props.menu.drawerOpen ? 'calc(100% - ' + drawerWidth + 'px)' : '100%'; // each child will be enclosed in this style const contentStyle = { width: width, marginTop: top + pad, marginLeft: left + pad, marginBottom: pad, marginRight: pad, padding:0 };
Creating a wrapper.
- An Appbar/nav
- A drawer
- It’s content
- The dynamic styling to take account of the drawer being open
export default class extends React.Component { render() { return ( <MenuWrapper> <AboutArticles /> </MenuWrapper> ); } }
The home page is a little different as it uses absolute positioning, so the styling for content won’t work. Its page component looks like this. Notice there is an additional property, fixed. This is going to be used by the wrapper class to know that this component has to be dealt with differently.
export default class Home extends React.Component { render() { return ( <MenuWrapper fixed={true}> <AppIntro /> </MenuWrapper> ); } }
The wrapper
- We can use this.props.children to reference the content that needs to be wrapped.
- The wrapped content is enclosed in a div that gets styled to take account of the drawer and appbar, and the wrapper also renders the appbar and drawer, meaning that the components referenced by this.props.children no longer need to care about margins, styling, drawers or appbars.
- If the item being wrapped is fixed positioning, then that approach doesn’t work. In this case we can use React.cloneElement() to pass additional parameters that can be dealt with inside.
@connect(store => { return { menu: store.menu }; }) export default class extends React.Component { render() { // adjust for left drawer and top, with a standard padding // for non fixed elements. const pad = 16; const appbarHeight = 64; const drawerWidth = 255; // this will be padding (or fixed positions) depending on whethere drawer is open const left = this.props.menu.drawerOpen ? drawerWidth : 0; const top = appbarHeight; const width = this.props.menu.drawerOpen ? 'calc(100% - ' + (drawerWidth+2*pad) + 'px)' : 'calc(100% - ' + (2*pad) + 'px)'; const contentWidth = this.props.menu.drawerOpen ? 'calc(100% - ' + drawerWidth + 'px)' : '100%'; // each child will be enclosed in this style const contentStyle = { width: width, marginTop: top + pad, marginLeft: left + pad, marginBottom: pad, marginRight: pad, padding:0 }; if (this.props.fixed) { // need to do special things to add props needed by fixed content return ( <div> <AppNav /> {React.cloneElement(this.props.children, { contentLeft:left, contentWidth:contentWidth, contentTop:top })} </div> ); } else { return ( <div style={contentStyle}> <AppNav /> {this.props.children} </div> ); } } }[ su_spacer size=”10″]
The Appbar and Drawer
<AppBar onLeftIconButtonTouchTap = {this.handleToggle} title={"Ephemeral Exchange Store"} style={{position:'fixed',left:0,top:0}} >
The drawer makes room for the Appbar height
<Drawer open={props.menu.drawerOpen} docked={true} containerStyle={{height: 'calc(100% - 64px)', top: 64}} >
It has it’s own scrollbar independent of the content, and its height is constrained to the height of the page.
For more like this, see React, redux, redis, material-UI and firebase. Why not join our forum, follow the blog or follow me on Twitter to ensure you get updates when they are available