CiriousJoker
CiriousJoker

Reputation: 582

<Link> can't be used together with MaterialUI

I wanted to create an Electron app with React and MaterialUI.

The interface looks pretty, but nothing happens when I click on the Drawer buttons. Routing is implemented, but whenever I try to insert a inside or around a MenuItem, it just throws

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of MenuBar.

Here's my code:

// App.js

import './assets/css/App.css';
import React, { Component } from 'react';
import { render } from 'react-dom';
import MenuBar from './components/MenuBar';

import { BrowserRouter as Router, Route, Link} from 'react-router-dom';

import About from "./screens/About";
import SomePage from "./screens/SomePage";

class App extends Component {
  render() {
    return (
      <Router>
        <div>
            <MenuBar></MenuBar>
        <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/somepage">Topics</Link></li>
        </ul>

        <hr/>

        <Route exact path="/"/>
        <Route path="/about" component={About}/>
        <Route path="/somepage" component={SomePage}/>
        </div>
    </Router>
    );
  }
}

export default App;

// MenuBar.js

// @flow weak

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withStyles, createStyleSheet } from 'material-ui/styles';
import Drawer from 'material-ui/Drawer';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import Divider from 'material-ui/Divider';
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List';
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';
import IconButton from 'material-ui/IconButton';
import MenuIcon from 'material-ui-icons/Menu';
import Menu, { MenuItem } from 'material-ui/Menu';

import MenuDots from './MenuDots';

// Routing
import { Link } from 'react-router'

// Icons
import InboxIcon from 'material-ui-icons/Inbox';
import DraftsIcon from 'material-ui-icons/Drafts';
import StarIcon from 'material-ui-icons/Star';
import SendIcon from 'material-ui-icons/Send';
import MailIcon from 'material-ui-icons/Mail';
import DeleteIcon from 'material-ui-icons/Delete';
import ReportIcon from 'material-ui-icons/Report';

const styleSheet = createStyleSheet({
  root: {
    width: '100%',
  },
  flex: {
    flex: 1,
  },
  list: {
    width: 250,
    flex: 'initial',
  },
  listFull: {
    width: 'auto',
    flex: 'initial',
  }
});

class MenuBar extends Component {
  constructor() {
    super();
    this.state = {
      open: false
    }
  }

  //Toggle function (open/close Drawer)
  toggleDrawer(_state) {
    if (_state === true || _state === false) {
      this.setState({
        open: _state
      })
    } else {
      this.setState({
        open: !this.state.open
      })
    }
  }

  handleLeftOpen = () => this.toggleDrawer(true);
  handleLeftClose = () => this.toggleDrawer(false);

  render() {
    const classes = this.props.classes;

    const mailFolderListItems = (
      <div>
        <ListItem button>
          <ListItemIcon>
            <InboxIcon />
          </ListItemIcon>
          <Link to="/somepage"><ListItemText primary="test" /></Link>
        </ListItem>
        <ListItem button>
          <ListItemIcon>
            <StarIcon />
          </ListItemIcon>
          <ListItemText primary="Starred" />
        </ListItem>
        <ListItem button>
          <ListItemIcon>
            <SendIcon />
          </ListItemIcon>
          <ListItemText primary="Send mail" />
        </ListItem>
        <ListItem button>
          <ListItemIcon>
            <DraftsIcon />
          </ListItemIcon>
          <ListItemText primary="Drafts" />
        </ListItem>
      </div>
    );

    const otherMailFolderListItems = (
      <div>
        <ListItem button>
          <ListItemIcon>
            <MailIcon />
          </ListItemIcon>
          <ListItemText primary="All mail" />
        </ListItem>
        <ListItem button>
          <ListItemIcon>
            <DeleteIcon />
          </ListItemIcon>
          <ListItemText primary="Trash" />
        </ListItem>
        <ListItem button>
          <ListItemIcon>
            <ReportIcon />
          </ListItemIcon>
          <ListItemText primary="Spam" />
        </ListItem>
      </div>
    );

    const sideList = (
      <div>
        <List className={classes.list} disablePadding>
          {mailFolderListItems}
        </List>
        <Divider />
        <List className={classes.list} disablePadding>
          {otherMailFolderListItems}
        </List>
      </div>
    );


    return (
      <div className={classes.root}>
        <AppBar position="static">
          <Toolbar style={{ paddingLeft: "8px", paddingRight: "8px" }}>
            <IconButton color="contrast" aria-label="Menu" onClick={this.toggleDrawer.bind(this)}>
              <MenuIcon />
            </IconButton>
            <Typography type="title" color="inherit" className={classes.flex}>
              Some Software
          </Typography>
            <Button color="contrast">Login</Button>
            <MenuDots></MenuDots>
          </Toolbar>
        </AppBar>
        <Drawer
          open={this.state.open}
          onRequestClose={this.handleLeftClose}
          onClick={this.handleLeftClose}
        >
          {sideList}
        </Drawer>
      </div>
    );
  }
}

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

export default withStyles(styleSheet)(MenuBar);

Upvotes: 5

Views: 15653

Answers (5)

Richard Hpa
Richard Hpa

Reputation: 2877

It's really simple actually. Nearly all of Material UIs components allow you to pass in a prop called component which overrides the default component being used. This then also allows you to pass in props such as to=""

import { Link as RouterLink } from 'react-router-dom';
// I just prefer to name it RouterLink since MUI has its own Link component
import { Typography } from '@mui/material';

<Typography component={RouterLink} to="/path">Text</Typography>

Upvotes: 1

Enrique Briones Arcos
Enrique Briones Arcos

Reputation: 1158

You can do this

<Typography className={aCustomClass}>
 <Link to='/path'>Your text</Link>
<Typography>

Upvotes: 2

If you want use Material UI styles and React router functionality u can use:

import { Link as UiLink } from '@material-ui/core';
import { Link } from 'react-router-dom';

//...
<UiLink component={Link} to="/path">Link text</UiLink>
//...

Upvotes: 1

CiriousJoker
CiriousJoker

Reputation: 582

Okay, so here's what I changed to achieve the following:

  • Routing
  • Styling as before (without Routing)

First, I had to change my imports from

import { Link } from 'react-router'

to

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

This allowed for better styling and more options. This is the code it boiled down to:

<NavLink to="/about" style={{ textDecoration: 'none', color: 'unset' }} >
  <ListItem button>
    <ListItemText primary="Test"/>
  </ListItem>
</NavLink >

textDecoration and color was necessary to remove the underline on links (because NavLink renders as a normal ) and to normalize the ripple effect.

Upvotes: 13

Mayank Shukla
Mayank Shukla

Reputation: 104429

You are using react router v4 so instead of importing Link from react-router in Menubar component import it from react-router-dom.

Like this:

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

Check the DOC.

Upvotes: 9

Related Questions