Material UIのヘッダー・フッターナビゲーションの実装【初学者のReact×Railsアプリ開発 第9回】

#やったこと
* Material UIを用いて、ヘッダー・フッターナビゲーションを実装した

#成果物
###画面の横幅が大きいとき
![スクリーンショット 2020-01-13 22.30.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367226/06bc060d-07ce-8e52-579f-0f8ae2ca15df.png)
###画面の横幅が小さいとき
![スクリーンショット 2020-01-13 22.29.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367226/02eb79e6-ccf9-98fd-0755-704dbf0c874e.png)

#参考にさせていただいたサイト
* こちらのコードを参考にさせていただきました。
[Reactで誰もがやりたかった10の機能。アプリ構想はあるけど作れない人の壁をぶっ壊す。](https://qiita.com/y_kawase/items/8f1b5a303400a09c4923#6-%E3%83%98%E3%83%83%E3%83%80%E3%83%BC%E3%83%95%E3%83%83%E3%82%BF%E3%83%BC%E3%83%8A%E3%83%93%E3%82%B2%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B7%E3%83%96%E3%81%ABresponsivedrawerbottomnavigation)

#実装手順
* 上の記事のコードを参考に改造しただけなので、今回はそんなに工夫点はないです。
* 上の参考記事をご覧ください。

###App.js
```javascript:App.js
import ResponsiveDrawer from './containers/ResponsiveDrawer';
import RouteRelatedBottomNavigation from './containers/RouteRelatedBottomNavigation';
class App extends Component {
  render() {
    return (
      <div className="App">
        <ResponsiveDrawer className="ResponsiveDrawer">
          <Switch>
            <Route path="/login" component={Login} />
            <Route path="/info" component={Info} />
            <Route path="/term" component={Term} />
            <Auth>
              <Switch>
                <Route exact path="/" component={Home} />
                <Route path='/create' component={Create} />
              </Switch>
            </Auth>
          </Switch>
        </ResponsiveDrawer>
        <RouteRelatedBottomNavigation />
      </div >
    );
  }
}
```

###containers/RouteRelatedBottomNavigation.js
```javascript:RouteRelatedBottomNavigation.js
import React from 'react';
import PropTypes from 'prop-types';

import { withStyles } from '@material-ui/core/styles';
import BottomNavigation from '@material-ui/core/BottomNavigation';
import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';

import HomeIcon from '@material-ui/icons/Home';
import InfoIcon from '@material-ui/icons/Info';

import { Link, withRouter } from 'react-router-dom';

const styles = theme => ({
  wrapper: {
    display: 'block',
    width: '100%',
    position: 'fixed',
    left: 0,
    bottom: 0,
    zIndex: 1000,
    textAlign: 'center',
  },
  root: {
    [theme.breakpoints.up('md')]: {
      display: 'none',
    },
  },
  button: {
    maxWidth: '100%',
  },
});


class RouteRelatedBottomNavigation extends React.Component {
  buttons_info = [
    { label: 'トップページ', icon: <HomeIcon />, link_to: '/' },
    { label: 'テーマ一覧', icon: <InfoIcon />, link_to: '/postslist' },
    { label: '投稿', icon: <InfoIcon />, link_to: '/create' },
  ];

  buttons = this.buttons_info.map((button_info, index) => {
    return (
      <BottomNavigationAction
        value={button_info.link_to}
        label={button_info.label}
        className={this.props.classes.button}
        icon={button_info.icon}
        component={Link}
        to={button_info.link_to}
      />
    );
  })

  render() {
    const { classes } = this.props;
    return (
      <div className={classes.wrapper}>
        <BottomNavigation
          value={this.props.location.pathname}
          showLabels
          className={classes.root}
          children={this.buttons}
        />
      </div>
    );
  }
}

RouteRelatedBottomNavigation.propTypes = {
  classes: PropTypes.object.isRequired,
};

export default withRouter(
  withStyles(styles, { withTheme: true })(RouteRelatedBottomNavigation)
);
```

###containers/ResponsiveDrawer.js
```javascript:ResponsiveDrawer.js
import React from 'react';
import PropTypes from 'prop-types';

import { withStyles } from '@material-ui/core/styles';
import Drawer from '@material-ui/core/Drawer';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import List from '@material-ui/core/List';
import IconButton from '@material-ui/core/IconButton';
import Hidden from '@material-ui/core/Hidden';
import Divider from '@material-ui/core/Divider';
import MenuIcon from '@material-ui/icons/Menu';
import Icon from '@material-ui/core/Icon';

import SettingsIcon from '@material-ui/icons/Settings';
import InfoIcon from '@material-ui/icons/Info';
import HomeIcon from '@material-ui/icons/Home';

// import titlepng from '../images/sukiraism_title.png'

import { Link } from 'react-router-dom';

import ResponsiveDrawerListItem from '../components/ResponsiveDrawerListItem';

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import * as actions from '../actions';

const drawerWidth = 240;
const headerNavigationHeight = 56;
const bottomNavigationHeight = 56;

const styles = theme => ({
  root: {
    flexGrow: 1,
    height: '100vh',
    zIndex: 1,
    overflow: 'hidden',
    position: 'relative',
    display: 'flex',
    width: '100%',
  },
  appBar: {
    position: 'absolute',
    marginLeft: drawerWidth,
    [theme.breakpoints.up('md')]: {
      width: `calc(100% - ${drawerWidth}px)`,
    },
  },
  toolBar: {
    justifyContent: 'space-between',
    minHeight: bottomNavigationHeight,
  },
  navIconHide: {
    [theme.breakpoints.up('md')]: {
      display: 'none',
    },
  },
  toolbar: theme.mixins.toolbar,
  drawerPaper: {
    width: drawerWidth,
    height: '100vh',
    [theme.breakpoints.up('md')]: {
      position: 'relative',
    },
  },
  content: {
    flexGrow: 1,
    backgroundColor: theme.palette.background.default,
    padding: theme.spacing.unit * 3,
    paddingTop: `calc(10px + ${headerNavigationHeight}px)`,
    paddingBottom: `calc(10px + ${bottomNavigationHeight}px)`,
    paddingLeft: 0,
    paddingRight: 0,
    [theme.breakpoints.up('md')]: {
      paddingBottom: 10,
    },
  },

  headerLogo: {
    display: 'flex',
    height: 40,
    width: 180,
  },
  iconLogo: {
    display: 'flex',
    height: 40,
    width: 40,
    borderRadius: '50%',
  },

  searchbar: {
    // flexGrow: 1,
  },
  flex: {
    flexGrow: 1,
  },
});

class ResponsiveDrawer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      mobileOpen: false,
    };
  }

  closeDrawerNav = () => {
    this.setState({ mobileOpen: false });
  }
  openDrawerNav = () => {
    this.setState({ mobileOpen: true });
  }

  renderUserlink() {
    const { CurrentUserReducer } = this.props;
    const { classes } = this.props;
    const image = CurrentUserReducer.items.image;
    const isLoggedin = CurrentUserReducer.isLoggedin;
    const nicknameLink = "/users/" + CurrentUserReducer.items.nickname;
    if (isLoggedin) {
      return (
        <Link to={nicknameLink}>
          <img src={image} alt="nickname_link" className={classes.iconLogo} />
        </Link>
      )
    } else {
      return (
        <a href={process.env.REACT_APP_API127_URL + "/api/v1/auth/twitter?auth_origin_url=" + process.env.REACT_APP_BASE_URL} >
          <Icon>add_circle</Icon>
        </a >

      )
    }
  }
  renderDeletewithCondition() {
    const { CurrentUserReducer } = this.props;
    const isLoggedin = CurrentUserReducer.isLoggedin;
    if (isLoggedin) {
      return (
        <List>
          <ResponsiveDrawerListItem
            to="/deleteaccount"
            onClick={this.closeDrawerNav}
            icon={<SettingsIcon />}
            text="アカウントの削除"
          />
        </List>
      )
    } else {

    }
  }
  renderLogoutwithCondition() {
    const { CurrentUserReducer } = this.props;
    const isLoggedin = CurrentUserReducer.isLoggedin;
    if (isLoggedin) {
      return (
        <List>
          <ResponsiveDrawerListItem
            to="/logout"
            onClick={this.closeDrawerNav}
            icon={<SettingsIcon />}
            text="ログアウト"
          />
        </List>
      )
    } else {

    }
  }

  render() {

    const { classes, theme, title, search } = this.props;
    const { CurrentUserReducer } = this.props;
    const isloggedin = CurrentUserReducer.isLoggedin;

    const drawer = (
      <div>
        <List>
          <ResponsiveDrawerListItem
            to=""
            onClick={this.closeDrawerNav}
            icon={<InfoIcon />}
            text="ホーム"
          />
        </List>
        <List>
          <ResponsiveDrawerListItem
            to="postslist"
            onClick={this.closeDrawerNav}
            icon={<InfoIcon />}
            text="テーマ一覧"
          />
        </List>
        <List>
          <ResponsiveDrawerListItem
            to="create"
            onClick={this.closeDrawerNav}
            icon={<InfoIcon />}
            text="テーマを投稿する"
          />
        </List>
        <List>
          <ResponsiveDrawerListItem
            to="search"
            onClick={this.closeDrawerNav}
            icon={<InfoIcon />}
            text="テーマを検索する"
          />
        </List>

        <Divider />
        <List>
          <ResponsiveDrawerListItem
            to="info"
            onClick={this.closeDrawerNav}
            icon={<InfoIcon />}
            text="XXXとは"
          />
        </List>
        <List>
          <ResponsiveDrawerListItem
            to="/term"
            onClick={this.closeDrawerNav}
            icon={<HomeIcon />}
            text="利用規約"
          />
        </List>
        <Divider />
        {this.renderDeletewithCondition()}
        {this.renderLogoutwithCondition()}
      </div>
    );

    return (
      <div className={classes.root}>
        <AppBar className={classes.appBar} position="fixed">
          <Toolbar className={classes.toolBar} variant="dense">
            <IconButton
              color="inherit"
              aria-label="Open drawer"
              onClick={() => this.openDrawerNav()}
              className={classes.navIconHide}
            >
              <MenuIcon />
            </IconButton>
            <Link to="/">
              <img src="#" width="150px" alt="title_logo" className={classes.headerLogo} />
            </Link>
            {this.renderUserlink()}
          </Toolbar>
        </AppBar>
        <Hidden mdUp>
          <Drawer
            variant="temporary"
            anchor={theme.direction === 'rtl' ? 'right' : 'left'}
            open={this.state.mobileOpen}
            onClose={this.closeDrawerNav}
            classes={{
              paper: classes.drawerPaper,
            }}
            ModalProps={{
              keepMounted: true, // Better open performance on mobile.
            }}
          >
            {drawer}
          </Drawer>
        </Hidden>
        <Hidden smDown implementation="css">
          <Drawer
            variant="permanent"
            open
            classes={{
              paper: classes.drawerPaper,
            }}
          >
            {drawer}
          </Drawer>
        </Hidden>
        <main className={classes.content}>
          {this.props.children}
        </main>
      </div>
    );
  }
}

ResponsiveDrawer.propTypes = {
  classes: PropTypes.object.isRequired,
  theme: PropTypes.object.isRequired,
};

const mapState = (state, ownProps) => ({
  CurrentUserReducer: state.CurrentUserReducer
});
function mapDispatch(dispatch) {
  return {
    actions: bindActionCreators(actions, dispatch),
  };
}

export default connect(mapState, mapDispatch)(
  withStyles(styles, { withTheme: true })(ResponsiveDrawer)
);
```

###components/ResponsiveDrawerListItem.js

```javascript:conponents/ResponsiveDrawerListItem.js
import React from 'react';
import PropTypes from 'prop-types';

import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';

import { Link } from 'react-router-dom'

const ResponsiveDrawerListItem = ({ to, onClick, icon, text }) => (
  <ListItem button component={Link} to={to} onClick={onClick}>
    <ListItemIcon>
      {icon}
    </ListItemIcon>
    <ListItemText primary={text} />
  </ListItem>
);

ResponsiveDrawerListItem.propTypes = {
  to: PropTypes.string.isRequired,
  onClick: PropTypes.func.isRequired,
  icon: PropTypes.object.isRequired,
  text: PropTypes.string.isRequired,
};

export default ResponsiveDrawerListItem;
```