web-dev-qa-db-fra.com

Material-ui ajoute un composant Link de react-router

J'ai du mal à ajouter le composant <Link/> À ma barre d'outils matérielle-ui

Ceci est ma classe de navigation:

class Navigation extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    var styles = {
      appBar: {
        flexWrap: 'wrap'
      },
      tabs: {
        width: '100%'
      }
    }

    return (
      <AppBar showMenuIconButton={false} style={styles.appBar}>
        <Tabs style={styles.tabs}>
          <Tab label='Most popular ideas'/>
          <Tab label='Latest ideas' />
          <Tab label='My ideas' />
        </Tabs>
      </AppBar>
    )
  }
}

Ce qui semble bien: Navbar

Les onglets sont cliquables, ont des animations fluides, c'est cool. Mais comment puis-je les relier avec react-router Et son composant '<Link/>?

J'ai essayé d'ajouter onChange auditeur de cette manière:

<Tab
  label='My ideas'
  onChange={<Link to='/myPath'></Link>}
/>

Cependant, j'obtiens l'erreur suivante:

Uncaught Invariant Violation: Expected onChange listener to be a function, instead got type object

Si j'essaie d'envelopper le composant <Tab/> Dans le composant <Link/>, J'obtiens une erreur si le composant <Tabs/> N'accepte que le composant <Tab/>.

Cela ne fonctionne pas non plus (aucune erreur n'est générée, mais cliquer sur Tabulation ne m'amène pas sur le chemin):

<Tab label='Most popular ideas'>
  <Link to='/popular'/>
</Tab>

Comment faire fonctionner le composant <Link/> Avec <Tabs> Et <AppBar>? Si ce n'est pas possible, je peux utiliser n'importe quel autre composant de la bibliothèque material-ui Pour former un menu approprié.

31
Evaldas Buinauskas

Pour Material UI 1.0 avec TypeScript: voir cet article par @ogglas ci-dessous.

Pour Material-UI 1.0 avec JS simple:

<Tabs value={value} onChange={this.handleChange}>
  {
    this.props.tabs.map(
      ({label, path})=><Tab key={label} 
                            label={label} 
                            className={classes.tabLink} 
                            component={Link} 
                            to={path} />
    )
  }
</Tabs>

Et classes.tabLink est défini comme:

tabLink : {
    display:"flex",
    alignItems:"center",
    justifyContent:"center"
}

Comment ça marche?

Tous les composants mui 1.0 héritant de ButtonBase prennent en charge un component prop, voir ButtonBase . L'idée est de vous permettre de contrôler le rendu du composant en tant qu'élément wrapper/root. Tab possède également cette fonctionnalité bien qu'au moment de l'écriture de cette réponse, cet accessoire ne soit pas documenté explicitement , mais en tant que Tab hérite de ButtonBase, tous ses accessoires sont reportés (et la documentation le couvre).

Une autre caractéristique de ButtonBase est que tous les accessoires supplémentaires, qui ne sont pas utilisés par ButtonBase ou par composant hérité, sont répartis sur le component spécifié. Nous avons utilisé ce comportement pour envoyer le to prop utilisé par Link en le donnant à Tab control. Vous pouvez envoyer des accessoires supplémentaires de la même manière. Notez que ceci est documenté explicitement pour ButtonBase et Tab.

Merci @ josh-l d'avoir demandé que cela soit ajouté.

41
hazardous

voici comment vous pouvez le faire maintenant:

<Tabs onChange={this.changeTab} value={value}>
   <Tab value={0} label="first" containerElement={<Link to="/first"/>} />
   <Tab value={1} label="second" containerElement={<Link to="/second"/>}/>
   <Tab value={2} label="third" containerElement={<Link to="/third"/>} />
 </Tabs>
28
high incompetance

Vous pouvez essayer cette méthode simple

 <Tab label='Most popular ideas'  to='/myPath' component={Link} />
6
Sarath Ak

Étant donné que nous utilisons TypeScript, je ne pouvais pas utiliser de solutions @rzardous. Voici comment nous avons implémenté le routage pour material-ui v1.0.0-beta.16 et react-router 4.2.0. La raison pour laquelle nous nous séparons this.props.history.location.pathname est parce que nous devons accéder à /renewals/123 par exemple. Si nous ne le faisions pas, nous aurions l'avertissement suivant et aucun onglet ne serait affiché comme actif: Warning: Material-UI: the value provided '/renewals/123' is invalid

Code complet avec les importations:

import * as React from "react";
import * as ReactDOM from "react-dom";
import * as ReactRouter from "react-router";
import * as PropTypes from "prop-types";
import { Switch, Route, Redirect, Link  } from "react-router-dom";
import { Cases } from './../Cases';
import { SidePane } from './../SidePane';
import { withStyles, WithStyles } from 'material-ui/styles';
import Paper from 'material-ui/Paper';
import Tabs, { Tab } from 'material-ui/Tabs';
import { withRouter } from "react-router-dom";
import Badge from 'material-ui/Badge';
import Grid from 'material-ui/Grid';
import { Theme } from 'material-ui/styles';
import SimpleLineIcons from '../../Shared/SimpleLineIcons'

interface IState {
    userName: string;
}

interface IProps {
    history?: any
}

const styles = (theme: Theme) => ({
    root: theme.typography.display1,
    badge: {
        right: '-28px',
        color: theme.palette.common.white,
    },
    imageStyle:{
        float: 'left',
        height: '40px',
        paddingTop: '10px'
    },
    myAccount: {
        float: 'right'
    },
    topMenuAccount: {
        marginLeft: '0.5em',
        cursor: 'pointer'
    }
});

type WithStyleProps = 'root' | 'badge' | 'imageStyle' | 'myAccount' | 'topMenuAccount';

class Menu extends React.Component<IProps & WithStyles<WithStyleProps>, IState> {
    constructor(props: IProps & WithStyles<WithStyleProps>) {
        super(props);
        this.state = {
            userName: localStorage.userName ? 'userName ' + localStorage.userName : ""
        }
    }
    componentDidMount() {
        this.setState({ userName: localStorage.userName ? localStorage.userName : "" })
    }
    logout(event: any) {
        localStorage.removeItem('token');
        window.location.href = "/"
    }

    handleChange = (event: any, value: any) => {
        this.props.history.Push(value);
    };

    render() {
        const classes = this.props.classes;
        let route = '/' + this.props.history.location.pathname.split('/')[1];
        return (
            <div>
                <Grid container spacing={24}>
                    <Grid item xs={12} className={classes.root}>
                        <img src="/Features/Client/Menu/logo.png" alt="Logo" className={classes.imageStyle} />
                        <div className={this.props.classes.myAccount}>
                        <span><span className={this.props.classes.topMenuAccount}>MY ACCOUNT</span><span className={classes.topMenuAccount}><SimpleLineIcons iconName={'user'} />&#x25BE;</span></span>
                            <span onClick={this.logout} className={classes.topMenuAccount}><SimpleLineIcons iconName={'logout'} /></span>
                        </div>
                    </Grid>
                    <Grid item xs={12} >
                        <div className="route-list">
                            <Tabs
                                value={route}
                                onChange={this.handleChange}
                                indicatorColor="primary"
                                textColor="primary"
                            >
                                <Tab label="Overview" value="/" />
                                <Tab label={<Badge classes={{ badge: classes.badge }} badgeContent={this.props.caseRenewalCount} color="primary">
                                    Renewals
                                   </Badge>} value="/renewals" />
                            </Tabs>
                        </div>
                    </Grid>
                </Grid>
            </div>
        );
    }
}
export default withStyles(styles)(withRouter(Menu))
6
Ogglas

Implémentation TypeScript des onglets gérés par le routeur.

Pour ceux qui recherchent l'implémentation TypeScript. Facile configurable. Piloté par la configuration tabs.

interface ITabsPageProps {
  match: match<{page: string}>;
  history: History;
}

const tabs = [{
  label: 'Fist Tab',
  link: 'fist-tab',
  component: <FirstTabContent/>
}, {
  label: 'Second Tab',
  link: 'second-tab',
  component: <SecondTabContent/>
}, {
  label: 'Third Tab',
  link: 'third-tab',
  component: <ThirdTabContent/>
}];

export class TabsPage extends React.Component<ITabsPageProps> {
  handleChange(tabLink: string) {
    this.props.history.Push(`/tabs-page/${tabLink}`);
  }

  render() {
    const currentTab = this.props.match.params.page;
    const selectedTab = tabs.find(tab => currentTab === tab.link);

    return (
      <Fragment>
        <Tabs
          value={currentTab}
          onChange={(event, value) => this.handleChange(value)}
        >
          {tabs.map(tab => (
            <Tab
              key={tab.link}
              value={tab.link}
              label={tab.label}
            />
          ))}
        </Tabs>

        {selectedTab && selectedTab.component}
      </Fragment>
    );
  }
}
3
Yaroslav Zaklinsky

Mon solution pour cette solution est donc assez fiable, même s’il s’agit peut-être d’une solution plus manuelle que ce que vous envisagez de faire.

La stratégie que j'ai utilisée est de ne même pas utiliser le composant lien. À la place, vous utiliserez la propriété Tabs onChange en tant que rappel capable de répondre aux clics sur les onglets et de suivre manuellement l'emplacement avec des accessoires sur le parent.

Vous pouvez importer un utilitaire appelé History from react-router qui vous permettra de transmettre manuellement des emplacements. Lors de l’utilisation de React-Router, votre arborescence de composants aura accès à la propriété Location qui a une clé de chemin d’accès avec la chaîne de votre emplacement actuel.

Nous allons analyser manuellement cette chaîne dans les composants qui constituent votre URL actuelle, puis utiliser une instruction Switch pour décider à la fois quel onglet est actuellement sélectionné et le lien vers lequel effectuer le lien lorsque vous cliquez sur un onglet. (Cela vous donne beaucoup de contrôle sur la navigation)

(par exemple ['', 'dernier'])

Voici un aperçu de ce que votre composant PEUT ressembler après l’intégration de cette solution.

import React from 'react';
import {History} from 'react-router';

function parseLocation(location) {
    if (String(location)) {
        var locationArray = location.split('/');
        return locationArray;
    } else {
        return false;
    }
};
function filterPath(path) {
    let locationArray = parseLocation(path);
    return locationArray[locationArray.length - 1];
};
var Navigation = React.createClass({
      mixins: [History],
      getPage() {
        if (this.props.location.pathname) {
          let pathname = this.props.location.pathname;
          let pageName = filterPath(pathname);
          return pageName;
        } else {
          return false;
        } 
      },
      decideContent() {
        let page = this.getPage();
        let content;
        switch(page) {
           case 'popular':
              content = 0;
           case 'latest':
              content = 1;
           case 'myideas':
              content = 2;
           default:
              content = 0;
        }
        return content;
      },
      handleTabChange(value) {
        let location = false;
        switch (value) {
           case 0:
             location = 'popular';
             break;
           case 1:
             location = 'latest';
             break;
           case 2:
             location = 'myideas';
             break;
        }
        if (location && location !== this.getPage()) {
          this.history.pushState(null, '/'+location);
        }
      },
      render() {
         var styles = {
          appBar: {
           flexWrap: 'wrap'
          },
          tabs: {
           width: '100%'
          }
         };
         let content = this.decideContent();
         let tabs = <Tabs
                  onChange={this.handleTabChange}
                  value={content}
                >
                  <Tab label="Most Popular Ideas" value={0}  />
                  <Tab label="Latest Ideas" value={1}  />
                  <Tab label="My Ideas" value={2}  />
                </Tabs>;
        return (
         <AppBar showMenuIconButton={false} style={styles.appBar}>
           {tabs}
         </AppBar>
        );
      }
});
3
Michael Lyons

Ceci est résolu en utilisant le <Link /> de Material-ui au lieu d’utiliser directement le <Link /> ou <NavLink /> de react-router. Vous trouverez l’exemple correspondant dans la documentation ici.

https://material-ui.com/components/links/

Également <Button />tag a un accessoire pour y parvenir

<Button color="inherit" component={Link} to={"/logout"}>Logout</Button>

Une discussion approfondie à ce sujet peut être trouvée ici

https://github.com/mui-org/material-ui/issues/85

1
Kishan B