React Router с Ant Design Sider: как заполнить раздел контента компонентами для соответствующего пункта меню - PullRequest
8 голосов
/ 10 января 2020

Я пытаюсь использовать боковой элемент меню 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, который я указал в качестве содержимого для пункта меню.

Я попробовал предложение Криса ниже - которое прекрасно работает для рендеринга компонента для каждого из различных элементов div содержимого элемента меню в сегменте макета, однако - недостатком этот подход заключается в том, что при каждой ссылке на страницу sh путь идет к компоненту контента для этого конкретного пункта меню, а не к тестовой странице, на которой есть меню, - и к компоненту контента с соответствующим контентом. Если этот подход одобрен как разумный, есть ли способ сохранить страницу refre sh на исходной странице, вместо попытки go перейти к URL-адресу пункта поднабора?

Обновить

Я нашел эту литературу . Я думаю, что это может быть то, что мне нужно знать, но язык слишком технический, чтобы я мог понять. Кто-нибудь может помочь с простой английской sh версией этого предмета?

Я также нашел этот урок , который использует Pose, чтобы помочь с рендерингом. Хотя эта структура - то, чего я пытаюсь достичь, похоже, что Поза устарела. Кто-нибудь знает, нужно ли go за пределами реагирующего маршрутизатора, чтобы получить такой результат, или есть решение в рамках реагирования, которое я могу реализовать?

Этот пример похож на что я хочу, но я не могу найти ничего, что определяет боковую панель (который, кажется, является аргументом, необходимым для этой работы).

Я также видел этот пост . Хотя некоторые ответы являются копиями и вставками документов, мне интересно, ближе ли ответ Адама Геринга к тому, что мне нужно. Я пытаюсь держать меню Sider на маршруте / da sh постоянно. Этот URL-адрес не должен изменяться - независимо от того, какой элемент меню на стороне пользователя щелкает пользователем, НО содержимое div в компоненте / da sh должно быть обновлено, чтобы отобразить компонент по указанному мною пути маршрута.

ПРИМЕНЕНИЕ ПРЕДЛОЖЕНИЯ КРИСА

Крис любезно предложил следующее предложение. Я попробовал это сделать, но обстоятельства, при которых он не работает должным образом, - это когда щелкают по пункту меню (и соответствующий компонент правильно загружается в div содержимого, но если я обновлю sh страницу, попытка refre sh попытается загрузить страницу с URL-адресом, предназначенным для компонента, который должен находиться внутри содержимого div на странице панели инструментов.

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

Я также нашел этот учебник - в котором используется тот же подход, который описан выше - и не перенаправляется так же, как мой код на странице refre sh. В учебнике вместо этого используется Route BrowserRouter - но в остальном я не вижу никаких отличий.

СЛЕДУЮЩАЯ ПОПЫТКА

Я видел этот пост (спасибо Мэтту). У меня есть попытался следовать рекомендациям в этом посте.

Я удалил внешнюю обертку Router со страницы Dashboard (вокруг приложения есть Router. js, где и настраивается маршрут к Dashboard).

Тогда в м y Dashboard, я изменил div Content на:

Я добавил:

let match = useRouteMatch();

к методу рендеринга внутри моего компонента Dashboard (как показано здесь ).

Я добавил useRouteMatch к своему оператору импорта отact-router-dom.

Это приводит к ошибке, которая говорит:

Ошибка: недопустимый хук вызов. Хуки могут быть вызваны только внутри тела функционального компонента. Это может произойти по одной из следующих причин: 1. У вас могут быть несовпадающие версии React и средства визуализации (например, React DOM) 2. Возможно, вы нарушаете правила хуков

В приведенной выше ошибке есть еще сообщение, но переполнение стека не позволит мне опубликовать его, потому что у него короткая ссылка

Я не знаю Не знаю, что я сделал не так на этих шагах, но если я закомментирую оператор let, страница загрузится.

Когда я go перехожу на / Dashboard и нажимаю "account", я ожидаю аккаунт Компонент для рендеринга внутри содержимого div у меня есть в макете на странице Dashboard. Вместо этого он перенаправляет непосредственно на страницу в localhost3000 / account (для которой нет ссылки на страницу - это просто компонент для отображения на странице Dashboard).

Так что это хуже, чем проблема, с которой я начал - потому что, по крайней мере, при запуске перенаправление происходило только на странице refre sh. Теперь это происходит немедленно.

Я нашел это репо , в котором есть примеры каждого вида маршрута. Я не вижу, что он делает, а я нет.

Этот пост , похоже, имел ту же проблему, что и я, и решил ее, используя тот же подход, что и я. Это сообщение от 2018 года, поэтому с течением времени этот подход может устареть, но я не вижу никакой разницы между тем, что реализует это решение, и моей первоначальной попыткой.

СЛЕДУЮЩАЯ ПОПЫТКА

Я думал, что нашел подход, который работает в ответе Любомира на этот пост .

У меня есть:

<Menu.Item key="2">
                      <Link to=

{${this.props.match.url}/account}> Настройки учетной записи

Затем в div в компоненте da sh, где я хочу отобразить этот компонент У меня есть:

<div>
                      <Switch>
                          <Route exact path={`${this.props.match.url}/:account`} component={Account} />

Это работает. На странице refre sh я могу продолжать работать как надо.

ОДНАКО, когда я добавляю второй пункт меню с:

<Menu.Item key="16">
                    <Link to={`${this.props.match.url}/learn`}>
                      <Icon type="solution" />
                      <span>Lern</span>
                    </Link>  
                  </Menu.Item>

вместо при рендеринге компонента Learning при щелчке по пункту меню для / learn (и изменение URL-адреса для обучения) отображается компонент Account. Если я удаляю отображение учетной записи из оператора switch, то у меня может отображаться правильный компонент Learning.

С этой попыткой у меня есть пункты меню, работающие в соответствии с расширением URL панели мониторинга (ie localhost / dash / account или localhost / dash / learn) только для пункта меню ONE . Если я добавлю второй элемент меню, единственный способ правильно отобразить 2-й компонент - это удалить первый элемент меню. Я использую переключатель с точными путями.

Я хочу, чтобы это решение работало с несколькими пунктами меню.

Я пробовал альтернативный путь для URL (например:

<Link to={`${this.props.match.url}/learn`}>

это то же самое, что и

<Link to={`${this.props.match.path}/learn`}>

Я прочитал объяснение в этом блоге , и хотя я не совсем понимаю эти опции. У меня прочитано это . Предполагается, что оператор match теперь является устаревшим методом рендеринга компонентов (теперь, когда доступны хуки). Я не могу найти никаких учебных материалов, которые показывают, как использовать хуки для достижения ожидаемого результата.

Ответы [ 2 ]

4 голосов
/ 10 января 2020

Лучшее решение - использовать React Router <Link>, чтобы каждый элемент меню связывался с указанным c путем, а затем с содержимым, используя <Switch> для рендеринга соответствующего компонента. Вот что делает c: 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

0 голосов
/ 19 января 2020

Я нашел ответ Любомира на этот пост. Оно работает. Вложенные маршруты с реагирующим маршрутизатором v4 / v5

Ссылка на пункт меню:

<Menu.Item key="2">
                      <Link to=
                       {`${this.props.match.url}/account`}>
                        Account
                      </Link>
                    </Menu.Item>

Путь отображения:

<Route exact path={`${this.props.match.path}/:account`} component={Account} />

Там это двоеточие перед именем компонента. Не уверен почему. Если кто-нибудь знает, как называется этот подход - я был бы благодарен за ссылку, чтобы я мог попытаться понять его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...