web-dev-qa-db-ja.com

リアクティブルーターからのMaterial-ui追加Linkコンポーネント

Material_ui AppBarに<Link/>コンポーネントを追加するのに苦労しています

これは私のナビゲーションクラスです。

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>
    )
  }
}

大丈夫に見える: Navbar

タブはクリック可能で、滑らかなアニメーションがあり、それは素晴らしいです。しかし、どのようにしてreact-routerおよびその<Link/>コンポーネントと接続しますか?

私はそのようなonChangeリスナーを追加しようとしました:

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

しかし、私は次のエラーが発生しています:

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

<Tab/>コンポーネントを<Link/>コンポーネントにラップしようとすると、<Tabs/>コンポーネントが<Tab/>コンポーネントのみを受け入れるというエラーが表示されます。

これも機能しません(エラーは発生していませんが、Tabをクリックしてもパスに移動しません):

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

<Link/>コンポーネントを<Tabs>および<AppBar>と連携させるにはどうすればよいですか?それが不可能な場合は、material-uiライブラリの他のコンポーネントを使用して適切なメニューを作成できます。

31

TypeScriptを使用したMaterial UI 1.0の場合:下記の@ogglasによる この投稿 を参照してください。

プレーンJSを使用したMaterial-UI 1.0の場合:

<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>

そして、classes.tabLink と定義されている:

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

これはどのように機能しますか?

ButtonBaseを継承するすべてのmui 1.0コンポーネントは、componentプロパティをサポートします。 ButtonBase を参照してください。アイデアは、コンポーネントがそのラッパー/ルート要素としてレンダリングするものを制御できるようにすることです。 Tabにもこの機能がありますが、この回答を書いている時点では、この小道具は明示的に文書化されていませんが、TabButtonBaseから継承します、そのすべての小道具が引き継がれます(そしてドキュメントはこれをカバーします)。

ButtonBaseの別の機能は、ButtonBaseまたは継承されたコンポーネントで使用されていないすべての追加の小道具が、指定されたcomponentに分散されることです。この動作を使用して、to制御に渡すことで、Linkが使用するTab propを送信しました。同じ方法で追加の小道具を送ることができます。これは、ButtonBaseTabの両方について明示的に文書化されていることに注意してください。

これを追加してくれてありがとう@ josh-l.

41
hazardous

ここでそれを行う方法を示します。

<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

この簡単な方法を試すことができます

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

TypeScriptを使用しているため、@ hazardousソリューションを使用できませんでした。これが、material-ui v1.0.0-beta.16およびreact-router 4.2.0のルーティングの実装方法です。 this.props.history.location.pathnameを分割する理由は、たとえば/renewals/123にアクセスする必要があるためです。これを行わなかった場合、次の警告が表示され、アクティブなタブは表示されません:Warning: Material-UI: the value provided '/renewals/123' is invalid

インポート付きの完全なコード:

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

ルーター駆動型タブのTypeScript実装。

TypeScript実装を探している人向け。簡単に設定可能。 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

したがって、このソリューションの私の回避策は非常に信頼できますが、あなたがやろうとしていることよりもソリューションのマニュアルになる可能性があります。

私が使用してきた戦略は、実際にはリンクコンポーネントさえ使用しないことです。代わりに、Tabクリックに応答できるコールバックとしてTabs onChangeプロパティを利用し、親の小道具で位置を手動で追跡します。

手動で場所をプッシュできるようにする反応ルーターから履歴と呼ばれるユーティリティをインポートできます。 React-Routerを使用している間、コンポーネントツリーは、現在の場所の文字列を含むパス名キーを持つLocationプロパティにアクセスできます。

この文字列を現在のURLを構成するコンポーネントに手動で解析し、S​​witchステートメントを使用して、現在選択されているタブと、タブがクリックされたときにリンクする場所の両方を決定します。 (これにより、ナビゲーションをかなり制御できます)

(例:[''、 'latest'])

以下は、このソリューションを統合した後のコンポーネントの外観のモックアップです。

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

これは、react-routerから<Link />または<Link />を直接使用する代わりに、material-uiからの<NavLink />を使用して解決されます。同じ例は、こちらのドキュメントに記載されています。

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

また、<Button />タグには、これを実現するコンポーネントpropがあります

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

これに関する詳細な議論はここで見つけることができます

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

1
Kishan B