javascript - React Router with - Ant Design Sider : how to populate content section with components for relevant menu item

标签 javascript reactjs react-router-dom antd

我正在尝试像标签面板一样使用 AntD 菜单侧边。

我想将组件放在内容中,以便在单击菜单项时内容面板呈现相关组件。

如何让这种结构将组件作为每个菜单项的内容?

import React from 'react';
import { compose } from 'recompose';
import { Divider, Layout, Card, Tabs, Typography, Menu, Breadcrumb, Icon } from 'antd';
import { PasswordForgetForm } from '../Auth/Password/Forgot';


const { Title } = Typography
const { TabPane } = Tabs;
const { Header, Content, Footer, Sider } = Layout;
const { SubMenu } = Menu;


class Dashboard extends React.Component {
  state = {
    collapsed: false,
  };

  onCollapse = collapsed => {
    console.log(collapsed);
    this.setState({ collapsed });
  };

  render() {
    return (
      <Layout style={{ minHeight: '100vh' }}>
        <Sider collapsible collapsed={this.state.collapsed} onCollapse={this.onCollapse}>
          <div  />
          <Menu theme="light" defaultSelectedKeys={['1']} mode="inline" >

            <Menu.Item key="1">
              <Icon type="fire" />
              <span>Next item</span>
              <PasswordForgetForm />
            </Menu.Item>  
            <Menu.Item key="2">
              <Icon type="fire" />
              <span>Next item</span>
              <Another component to render in the content div />
            </Menu.Item>

          </Menu>
        </Sider>
        <Layout>
          <Header style={{ background: '#fff', padding: 0 }} />
          <Content style={{ margin: '0 16px', background: '#fff' }}>

            <div style={{ padding: 24, background: '#fff', minHeight: 360 }}>
            RENDER RELEVANT COMPONENT HERE BASED ON MENU ITEM SELECTED
            </div>
          </Content>
          </Layout>
      </Layout>
    );
  }
}

export default Dashboard;

当我尝试呈现它时,不会抛出任何错误,但内容 div 不会使用我指定为菜单项内容的 PasswordForgetForm 进行更新。

我在下面尝试了 Chris 的建议 - 它可以很好地为布局段中的每个不同菜单项内容 div 呈现组件,但是 - 这种方法的缺点是每次页面刷新时,路径都会转到内容组件那个特定的菜单项,而不是上面有菜单的测试页面——以及具有相关内容的内容组件。如果这种方法被认可为明智的,有没有办法在原始页面上保持页面刷新,而不是尝试转到子菜单项 url?

更新

我找到了这个 literature .我想这可能是我需要知道的,但是语言太技术性了,我无法理解。任何人都可以帮助解决这个主题的简单英文版本吗?

我还找到了 this tutorial它使用姿势来帮助渲染。虽然这种结构是我想要实现的,但看起来 Pose 已被弃用。有谁知道是否有必要超越 react-router 来获得这个结果,或者 react 中是否有我可以寻求实现的解决方案?

example与我想要的类似,但我找不到任何定义侧边栏的东西(这似乎是使这项工作所必需的参数)。

我也看过this post .虽然有些答案是文档的复制和粘贴,但我想知道 Adam Gering 的答案是否更接近我的需要。我试图始终将 Sider 菜单保留在/dash 路线上。该 url 不应更改 - 无论用户单击侧边中的哪个菜单项,都应更新/dash 组件中的内容 div 以在我指定的路由路径上呈现组件。

应用克里斯的建议

克里斯在下面友好地提出了一个建议。我试过了,但它没有按预期执行的情况是单击菜单项时(并且相关组件在内容 div 中正确加载,但是如果我刷新页面,刷新尝试加载一个页面用于应该位于仪表板页面上的内容 div 内的组件的 url。
import React from 'react';
import {
    BrowserRouter as Router,
    Route,
    Link,
    Switch,

  } from 'react-router-dom';
import * as ROUTES from '../../constants/Routes';
import { compose } from 'recompose';
import { Divider, Layout, Card, Tabs, Typography, Menu, Breadcrumb, Icon } from 'antd';
import { withFirebase } from '../Firebase/Index';
import { AuthUserContext, withAuthorization, withEmailVerification } from '../Session/Index';

// import UserName from '../Users/UserName';
import Account from '../Account/Index';
import Test from '../Test/Index';



const { Title } = Typography
const { TabPane } = Tabs;
const { Header, Content, Footer, Sider } = Layout;
const { SubMenu } = Menu;


class Dashboard extends React.Component {
  state = {
    collapsed: false,
  };

  onCollapse = collapsed => {
    console.log(collapsed);
    this.setState({ collapsed });
  };

  render() {
    const {  loading } = this.state;
    // const dbUser = this.props.firebase.app.snapshot.data();
    // const user = Firebase.auth().currentUser;
    return (
    <AuthUserContext.Consumer>
      {authUser => (  

        <div>    
         {authUser.email}
          <Router>  
            <Layout style={{ minHeight: '100vh' }}>
              <Sider collapsible collapsed={this.state.collapsed} onCollapse={this.onCollapse}>
                <div  />
                <Menu theme="light" defaultSelectedKeys={['1']} mode="inline" >
                  <SubMenu
                    key="sub1"
                    title={
                      <span>
                      <Icon type="user" />
                      <span>Profile</span>
                      </span>
                    }
                  >

                      <Menu.Item key="2"><Link to={ROUTES.ACCOUNT}>Account Settings</Link></Menu.Item>


                      <Menu.Item key="3"><Link to={ROUTES.TEST}>2nd content component</Link></Menu.Item>
</SubMenu>

                </Menu>
              </Sider>
              <Layout>

                <Header>                </Header>      
                <Content style={{ margin: '0 16px', background: '#fff' }}>

                  <div style={{ padding: 24, background: '#fff', minHeight: 360 }}>
                      <Switch>
                          <Route path={ROUTES.ACCOUNT}>
                          <Account />
                          </Route>
                          <Route path={ROUTES.TEST}>
                          < Test />
                          </Route>

                      </Switch>    
                  </div>
                </Content>
                <Footer style={{ textAlign: 'center' }}>


                 test footer
                </Footer>
              </Layout>
            </Layout>
          </Router>  
        </div>
      )}
    </AuthUserContext.Consumer>  
    );
  }
}

export default Dashboard;

在我的路线文件中,我有:
export const ACCOUNT = '/account';

我还找到了 this tutorial - 使用上面概述的相同方法 - 并且不会以与我的代码在页面刷新时相同的方式重定向。本教程使用 Route 而不是 BrowserRouter - 但除此之外我看不出任何区别。

下一次尝试

我看到这个 post (感谢马特)。我试图遵循该帖子中的建议。

我从仪表板页面中删除了外部路由器包装器(App.js 周围有一个路由器,这是设置仪表板路由的地方)。

然后,在我的仪表板中,我将内容 div 更改为:

我补充说:
let match = useRouteMatch();

到我的仪表板组件内的渲染方法(如图所示 here )。

我在 react-router-dom 的 import 语句中添加了 useRouteMatch。

这会产生一个错误,指出:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks



上面的错误中有更多消息,但堆栈溢出不允许我发布它,因为它有一个短链接

我不知道我在这些步骤中做错了什么,但是如果我注释掉 let 语句,页面就会加载。

当我转到/仪表板并单击“帐户”时,我希望帐户组件在仪表板页面布局中的内容 div 内呈现。相反,它直接重定向到位于 localhost3000/account 的页面(没有页面引用——它只是一个在仪表板页面内呈现的组件)。

所以这比我开始的问题更糟糕 - 因为至少在开始时,重定向只发生在页面刷新时。现在它立即发生。

我找到了 this repo有每种路线的例子。我看不出它在做什么而我不是。

post似乎和我有同样的问题,并使用与我尝试过的方法相同的方法解决了它。这是 2018 年的帖子,所以时间的流逝可能会使该方法过时 - 但我看不出该帖子解决方案实现的内容与我最初的尝试之间有任何区别。

下一次尝试

我以为我已经找到了一种适用于 Lyubomir 对 this post 的回答的方法。 .

我有:
<Menu.Item key="2">
                      <Link to=

{ ${this.props.match.url}/account }>
帐号设定



然后在我想显示这个组件的破折号组件的 div 中,我有:
<div>
                      <Switch>
                          <Route exact path={`${this.props.match.url}/:account`} component={Account} />

这有效。在页面刷新时,我可以保持正常工作。

但是,当我添加第二个菜单项时:
<Menu.Item key="16">
                    <Link to={`${this.props.match.url}/learn`}>
                      <Icon type="solution" />
                      <span>Lern</span>
                    </Link>  
                  </Menu.Item>

当单击/learn 的菜单项(并且 url 栏更改为学习)时,不是呈现 Learning 组件,而是呈现 Account 组件。如果我从 switch 语句中删除帐户显示,那么我可以显示正确的学习组件。

通过这次尝试,我的菜单项可以匹配 的仪表板 url 扩展名(即 localhost/dash/account 或 localhost/dash/learn)。一仅菜单项。如果我添加第二个菜单项,我可以正确呈现第二个组件的唯一方法是删除第一个菜单项。我正在使用具有精确路径的开关。

我希望此解决方案适用于多个菜单项。

我已经尝试过 url 的交替路径(例如:
<Link to={`${this.props.match.url}/learn`}>

是相同的:
<Link to={`${this.props.match.path}/learn`}>

我已阅读 this blog 中的说明虽然我并不完全理解这些选项。我有 read this .它表明 match 语句现在是渲染组件的传统方法(现在钩子(Hook)可用)。我找不到任何说明如何使用钩子(Hook)来实现预期结果的培训 Material 。

最佳答案

更好的解决方案是使用 React Router <Link>使每个菜单项链接到特定路径,然后在内容中,使用 <Switch>来渲染相应的组件。这是文档:React router

使用 React Router 渲染

<Router>
  <Layout style={{ minHeight: "100vh" }}>
    <Sider
      collapsible
      collapsed={this.state.collapsed}
      onCollapse={this.onCollapse}
    >
      <div />
      <Menu theme="light" defaultSelectedKeys={["1"]} mode="inline">
        <Menu.Item key="1">
          // Add a react router link here
          <Link to="/password-forget-form">
            <Icon type="fire" />
            <span>Next item</span>
          </Link>
        </Menu.Item>
        <Menu.Item key="2">
          // Add another react router link
          <Link to="/next-item">
            <Icon type="fire" />
            <span>Next item</span>
          </Link>
        </Menu.Item>
      </Menu>
    </Sider>
    <Layout>
      <Header style={{ background: "#fff", padding: 0 }} />
      <Content style={{ margin: "0 16px", background: "#fff" }}>
        <div style={{ padding: 24, background: "#fff", minHeight: 360 }}>
          // Render different components based on the path
          <Switch>
            <Route path="/password-forget-form">
              <PasswordForgetForm />
            </Route>
            <Route path="/next-item">
              <Another component to render in the content div />
            </Route>
          </Switch>
        </div>
      </Content>
    </Layout>
  </Layout>
</Router>;

使用菜单键渲染

应用程序.js
import React, { useState } from "react";
import { Layout } from "antd";
import Sider from "./Sider";
import "./styles.css";

const { Content } = Layout;
export default function App() {
  const style = {
    fontSize: "30px",
    height: "100%",
    display: "flex",
    alignItems: "center",
    justifyContent: "center"
  };

  const components = {
    1: <div style={style}>Option 1</div>,
    2: <div style={style}>Option 2</div>,
    3: <div style={style}>Option 3</div>,
    4: <div style={style}>Option 4</div>
  };

  const [render, updateRender] = useState(1);

  const handleMenuClick = menu => {
    updateRender(menu.key);
  };

  return (
    <div className="App">
      <Layout style={{ minHeight: "100vh" }}>
        <Sider handleClick={handleMenuClick} />
        <Layout>
          <Content>{components[render]}</Content>
        </Layout>
      </Layout>
    </div>
  );
}

Sider.js
import React from "react";
import { Menu, Layout, Icon } from "antd";

const { SubMenu } = Menu;

export default function Sider(props) {
  const { handleClick } = props;
  return (
    <Layout.Sider>
      <Menu theme="dark" mode="inline" openKeys={"sub1"}>
        <SubMenu
          key="sub1"
          title={
            <span>
              <Icon type="mail" />
              <span>Navigation One</span>
            </span>
          }
        >
          <Menu.Item key="1" onClick={handleClick}>
            Option 1
          </Menu.Item>
          <Menu.Item key="2" onClick={handleClick}>
            Option 2
          </Menu.Item>
          <Menu.Item key="3" onClick={handleClick}>
            Option 3
          </Menu.Item>
          <Menu.Item key="4" onClick={handleClick}>
            Option 4
          </Menu.Item>
        </SubMenu>
      </Menu>
    </Layout.Sider>
  );
}

Edit antd-menu-click-rendering

关于javascript - React Router with - Ant Design Sider : how to populate content section with components for relevant menu item,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59674690/

相关文章:

javascript - Mongoose find 使用expressjs查找if语句错误

node.js - Node.js 和 React 之间的 Websocket 握手错误

javascript - 设置鳕鱼技能水平 (UCI/Javascript)

javascript - 使用循环从带有拼接的数组中选择项目

javascript - 使用 Python 和 JavaScript 通过 Selenium 使 WebElement 可见

javascript - 使用 javascript 的表单需要字段吗?

javascript - 如何从 URL 中删除/替换某些内容?

javascript - 将 'react-router' 迁移到 'react-router-dom' (v4)

javascript - 条件满足后私有(private)路由不重定向

reactjs - 为什么我的 React 组件总是显示相同的状态?