Javascript 从react router添加链接组件的材质ui

Javascript 从react router添加链接组件的材质ui,javascript,reactjs,react-router,material-ui,Javascript,Reactjs,React Router,Material Ui,我正在努力将组件添加到我的材质ui AppBar中 这是我的导航课: class Navigation extends Component { constructor(props) { super(props) } render() { var styles = { appBar: { flexWrap: 'wrap' }, tabs: { width: '100%' } }

我正在努力将
组件添加到我的材质ui AppBar中

这是我的导航课:

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>
    )
  }
}
但是,我得到了以下错误:

Uncaught Invariant Violation: Expected onChange listener to be a function, instead got type object
如果我试图将
组件包装到
组件中,我得到的错误是
组件只接受
组件

这也不起作用(不会产生错误,但单击Tab不会将我带到路径):



如何使
组件与
一起工作?如果这是不可能的,我可以使用
材质ui
库中的任何其他组件来形成适当的菜单。

因此,我针对该解决方案的工作非常可靠,尽管它可能比您希望做的更手动

我一直使用的策略是实际上甚至不使用链接组件。相反,您将使用Tabs onChange属性作为回调,它可以响应选项卡单击,并使用父级上的道具手动跟踪位置

您可以从react router导入名为History的实用程序,该实用程序将允许您手动推送位置。使用React Router时,组件树将可以访问具有路径名键和当前位置字符串的Location prop

我们将手动将此字符串解析为组成当前URL的组件,然后使用Switch语句来决定当前选择的选项卡以及单击选项卡时链接到的位置。(这给了您相当大的导航控制权)

(例如,[“最新”])

下面是集成此解决方案后组件的外观模型

从“React”导入React;
从“react router”导入{History};
函数解析位置(位置){
if(字符串(位置)){
var locationArray=location.split('/');
返回定位阵列;
}否则{
返回false;
}
};
函数筛选器路径(路径){
让locationArray=parseLocation(路径);
返回locationArray[locationArray.length-1];
};
var Navigation=React.createClass({
mixins:[历史],
getPage(){
if(this.props.location.pathname){
让pathname=this.props.location.pathname;
让pageName=filterPath(路径名);
返回页面名;
}否则{
返回false;
} 
},
十进制内容(){
让page=this.getPage();
让内容;
交换机(第页){
“流行”一案:
内容=0;
最新个案:
内容=1;
“我的想法”案例:
内容=2;
违约:
内容=0;
}
返回内容;
},
handleTabChange(值){
让位置=假;
开关(值){
案例0:
地点='流行';
打破
案例1:
地点=‘最新’;
打破
案例2:
位置='myideas';
打破
}
if(location&&location!==this.getPage()){
this.history.pushState(null,“/”+位置);
}
},
render(){
变量样式={
appBar:{
flexWrap:“wrap”
},
选项卡:{
宽度:“100%”
}
};
让content=this.decideContent();
设制表符=
;
返回(
{tabs}
);
}
});

以下是您现在可以执行的操作:

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

对于带有Typescript的材料UI 1.0:请参见下面的@ogglas

对于带有纯JS的材质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>
这是怎么回事? 从
按钮库继承的所有mui 1.0组件都支持
组件
道具,请参阅。其思想是允许您控制组件作为其包装器/根元素呈现的内容<代码>选项卡
也具有此功能,尽管在编写此答案时,此道具没有明确记录,但由于
选项卡
继承自
按钮库
,因此其所有道具都会保留下来(文档中确实涵盖了这一点)

ButtonBase
的另一个特点是,所有未被
ButtonBase
或继承组件使用的额外道具都分布在指定的
组件上。我们已使用此行为将
发送到
链接使用的
道具,方法是将其发送到
选项卡
控件。你可以用同样的方式发送任何附加道具。请注意,这是为
按钮库
选项卡
明确记录的


感谢@josh-l请求添加此内容。

因为我们使用的是TypeScript,所以我无法使用@hazardous solutions。这就是我们为
材质ui v1.0.0-beta.16
反应路由器4.2.0
实现路由的方式。我们之所以拆分
this.props.history.location.pathname
是因为我们需要访问
/renewals/123
。如果不这样做,我们将得到以下警告,并且没有选项卡显示为活动状态:
警告:物料UI:提供的值“/renewals/123”无效

使用导入完成代码:

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))
import*as React from“React”;
从“react dom”导入*作为react dom;
从“反应路由器”导入*作为反应路由器;
从“道具类型”导入*作为道具类型;
从“react router dom”导入{交换机、路由、重定向、链接};
从“/../Cases”导入{Cases};
从“/../SidePane”导入{SidePane};
从“材质ui/样式”导入{withStyles,withStyles};
从“物料界面/纸张”导入纸张;
从“物料界面/选项卡”导入选项卡,{Tab};
从“react router dom”导入{withRouter};
从“材料界面/徽章”导入徽章;
从“物料界面/网格”导入网格;
从“材质ui/样式”导入{Theme};
<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>
tabLink : {
    display:"flex",
    alignItems:"center",
    justifyContent:"center"
}
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))
 <Tab label='Most popular ideas'  to='/myPath' component={Link} />
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>
    );
  }
}
<Button color="inherit" component={Link} to={"/logout"}>Logout</Button>
import * as React from "react";
import { BrowserRouter as Router, Route, Redirect, Switch, Link, LinkProps } from 'react-router-dom';
import AppBar from '@material-ui/core/AppBar';
import Tabs from '@material-ui/core/Tabs';
import { default as Tab, TabProps } from '@material-ui/core/Tab';

import Home from './Home';
import ProductManagement from './ProductManagement';
import Development from './Development';
import HomeIcon from '@material-ui/icons/Home';
import CodeIcon from '@material-ui/icons/Code';
import TimelineIcon from '@material-ui/icons/Timeline';

const LinkTab: React.ComponentType<TabProps & LinkProps> = Tab as React.ComponentType<TabProps & LinkProps>;

function NavBar() {
  const [value, setValue] = React.useState(0);

  const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
    setValue(newValue);
  };
  return (
    <div >
      <AppBar position="static" >
        <Tabs value={value} onChange={handleChange} centered>
          <LinkTab label='Home' icon={ <HomeIcon />} component={Link} to="/" />
          <LinkTab label='Development' icon={<CodeIcon />} component={Link} to="/dev" />
          <LinkTab label='Product Management' icon={<TimelineIcon />} component={Link} to="/pm" />
        </Tabs>
      </AppBar>
    </div>
  )
};

export default function App() {
  return (
    <Router>
      <div>
        <NavBar />
        <Switch>
          <Route exact path="/" component={ Home } />
          <Route exact path="/dev" component={ Development } />
          <Route exact path="/pm" component={ ProductManagement } />
          <Redirect from="/" to="/" />
        </Switch>
      </div>
    </Router>
  )
}