Reagieren Sie mit - Ant Design Sider auf Router: So füllen Sie den Inhaltsbereich mit Komponenten für den entsprechenden Menüpunkt


8

Ich versuche, AntD Menüsider wie ein Registerkartenfeld zu verwenden.

Ich möchte Komponenten in den Inhalt einfügen, damit das Inhaltsfenster die zugehörige Komponente rendert, wenn auf ein Menüelement geklickt wird.

Wie kann ich diese Struktur dazu bringen, Komponenten als Inhalt für jeden Menüpunkt zu verwenden?

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;

Wenn ich versuche, dies zu rendern, werden keine Fehler ausgegeben, aber der Inhaltsbereich wird nicht mit dem PasswordForgetForm aktualisiert, das ich als Inhalt für das Menüelement angegeben habe.

Ich habe den folgenden Vorschlag von Chris ausprobiert - der gut funktioniert, um die Komponente für jeden der verschiedenen Menüelement-Inhaltsbereiche im Layoutsegment zu rendern. Der Nachteil dieses Ansatzes ist jedoch, dass bei jeder Seitenaktualisierung der Pfad zur Inhaltskomponente für geht dieser bestimmte Menüpunkt anstelle der Testseite, auf der sich das Menü befindet - und die Inhaltskomponente mit diesem relevanten Inhalt. Wenn dieser Ansatz als sinnvoll eingestuft wird, gibt es eine Möglichkeit, die Seitenaktualisierung auf der Originalseite beizubehalten, anstatt zu versuchen, zu einer URL eines Teilmengenmenüelements zu wechseln?

Aktualisieren

Ich habe diese Literatur gefunden . Ich denke, das ist vielleicht das, was ich wissen muss, aber die Sprache ist zu technisch, als dass ich sie verstehen könnte. Kann jemand mit einer einfachen englischen Version dieses Themas helfen?

Ich habe auch dieses Tutorial gefunden , das Pose verwendet, um beim Rendern zu helfen. Während ich versuche, diese Struktur zu erreichen, sieht es so aus, als wäre Pose veraltet. Weiß jemand, ob es notwendig ist, über den React-Router hinauszugehen, um dieses Ergebnis zu erzielen, oder gibt es eine Lösung in React, die ich implementieren kann?

Dieses Beispiel ähnelt dem, was ich möchte, aber ich kann nichts finden, das die Seitenleiste definiert (was ein Argument zu sein scheint, das notwendig ist, damit dies funktioniert).

Ich habe diesen Beitrag auch gesehen . Während einige der Antworten ein Kopieren und Einfügen der Dokumente sind, frage ich mich, ob die Antwort von Adam Gering näher an dem liegt, was ich brauche. Ich versuche, das Sider-Menü immer auf der / dash-Route zu halten. Diese URL sollte sich nicht ändern - unabhängig davon, auf welchen Menüpunkt im Sider der Benutzer klickt, ABER der Inhaltsbereich in der Komponente / dash sollte aktualisiert werden, um die Komponente auf dem von mir angegebenen Routenpfad zu rendern.

CHRIS 'VORSCHLAG ANWENDEN

Chris hat freundlicherweise einen Vorschlag unten angeboten. Ich habe es versucht, aber der Umstand, dass es nicht wie gewünscht funktioniert, ist, wenn auf das Menüelement geklickt wird (und die entsprechende Komponente korrekt in den Inhaltsbereich geladen wird, aber wenn ich die Seite aktualisiere, versucht die Aktualisierung, eine Seite mit einem zu laden URL für die Komponente, die sich innerhalb des Inhaltsbereichs auf der Dashboard-Seite befinden soll.

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;

In meiner Routendatei habe ich:

export const ACCOUNT = '/account';

Ich habe auch dieses Tutorial gefunden, das denselben oben beschriebenen Ansatz verwendet und nicht auf die gleiche Weise umgeleitet wird, wie mein Code bei der Seitenaktualisierung. Das Tutorial verwendet Route anstelle von BrowserRouter - ansonsten sehe ich keine Unterschiede.

NÄCHSTER VERSUCH

Ich habe diesen Beitrag gesehen (danke Matt). Ich habe versucht, den Vorschlägen in diesem Beitrag zu folgen.

Ich habe den äußeren Router-Wrapper von der Dashboard-Seite entfernt (um App.js herum befindet sich ein Router, auf dem die Route zum Dashboard eingerichtet wird).

Dann habe ich in meinem Dashboard den Inhaltsbereich geändert in:

Ich fügte hinzu:

let match = useRouteMatch();

auf die Rendermethode in meiner Dashboard-Komponente (wie hier gezeigt ).

Ich habe meiner Importanweisung von react-router-dom useRouteMatch hinzugefügt.

Dies erzeugt einen Fehler, der besagt:

Fehler: Ungültiger Hook-Aufruf. Hooks können nur innerhalb des Körpers einer Funktionskomponente aufgerufen werden. Dies kann aus einem der folgenden Gründe geschehen: 1. Möglicherweise stimmen Versionen von React und des Renderers nicht überein (z. B. React DOM). 2. Möglicherweise verstoßen Sie gegen die Hook-Regeln

Der obige Fehler enthält weitere Meldungen, aber beim Stapelüberlauf kann ich ihn nicht veröffentlichen, da er einen kurzen Link enthält

Ich weiß nicht, was ich in diesen Schritten falsch gemacht habe, aber wenn ich die let-Anweisung auskommentiere, wird die Seite geladen.

Wenn ich zu / Dashboard gehe und auf "Konto" klicke, erwarte ich, dass die Kontokomponente innerhalb des Inhaltsbereichs gerendert wird, den ich im Layout auf der Dashboard-Seite habe. Stattdessen wird direkt zu einer Seite unter localhost3000 / account umgeleitet (für die es keinen Seitenverweis gibt - es ist nur eine Komponente, die innerhalb der Dashboard-Seite gerendert werden muss).

Das ist also schlimmer als das Problem, mit dem ich angefangen habe - denn zumindest zu Beginn erfolgte die Umleitung nur bei der Seitenaktualisierung. Jetzt passiert es sofort.

Ich habe dieses Repo gefunden , das Beispiele für jede Art von Route enthält. Ich kann nicht sehen, was es tut, was ich nicht bin.

Dieser Beitrag scheint das gleiche Problem wie ich gehabt zu haben und hat es mit dem gleichen Ansatz gelöst, den ich versucht habe. Es ist ein Beitrag aus dem Jahr 2018, daher ist der Ansatz im Laufe der Zeit möglicherweise veraltet - aber ich kann keinen Unterschied zwischen der Implementierung dieser Beitragslösung und meinem ursprünglichen Versuch feststellen.

NÄCHSTER VERSUCH

Ich hatte gedacht, ich hätte einen Ansatz gefunden, der in Lyubomirs Antwort auf diesen Beitrag funktioniert .

Ich habe:

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

{ ${this.props.match.url}/account}> Kontoeinstellungen

Dann in der div auf der Dash-Komponente, wo ich diese Komponente anzeigen möchte, habe ich:

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

Das funktioniert. Beim Aktualisieren der Seite kann ich dafür sorgen, dass die Dinge so funktionieren, wie sie sollten.

JEDOCH, wenn ich einen zweiten Menüpunkt hinzufüge mit:

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

Anstatt die Lernkomponente zu rendern, wenn auf den Menüpunkt für / learn geklickt wird (und die URL-Leiste zum Lernen geändert wird), wird die Kontokomponente gerendert. Wenn ich die Kontoanzeige aus der switch-Anweisung lösche, kann ich die richtige Lernkomponente anzeigen lassen.

Bei diesem Versuch funktionieren die Menüelemente nur für EIN Menüelement mit einer Dashboard-URL-Erweiterung (dh localhost / dash / account oder localhost / dash / learn) . Wenn ich einen zweiten Menüpunkt hinzufüge, kann ich die zweite Komponente nur korrekt rendern, indem ich den ersten Menüpunkt lösche. Ich benutze Schalter mit genauen Pfaden.

Ich möchte, dass diese Lösung mit mehreren Menüelementen funktioniert.

Ich habe versucht, den Pfad für die URL zu wechseln (z. B.:

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

ist das gleiche wie:

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

Ich habe die Erklärung in diesem Blog gelesen und verstehe diese Optionen zwar nicht ganz. Ich habe diese Zeilen lesen . Es wird vorgeschlagen, dass die Match-Anweisung jetzt eine Legacy-Methode zum Rendern von Komponenten ist (jetzt, da Hooks verfügbar sind). Ich kann keine Schulungsunterlagen finden, die zeigen, wie man mit Haken das erwartete Ergebnis erzielt.


Bitte geben Sie auch die PasswordForgottenFormKomponente an.
AleksandrH

Es ist nur eine konstante Komponente mit einem Formular. Jeder Menüpunkt verfügt über eine separate Komponente (einige sind Klassenkomponenten), die im Inhaltsbereich des Layoutabschnitts der Testkomponente gerendert wird.
Mel

Antworten:


4

Die bessere Lösung besteht darin, mit React Router <Link>jedes Menüelement mit einem bestimmten Pfad zu verknüpfen und dann im Inhalt <Switch>die entsprechende Komponente zu rendern. Hier ist das Dokument: React Router

Mit React Router rendern

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

Mit Menütasten rendern

App.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>
  );
}

Bearbeiten Sie das Antd-Menü-Klick-Rendering


Kommentare sind nicht für eine ausführliche Diskussion gedacht. Dieses Gespräch wurde in den Chat verschoben .
Samuel Liew

Vielen Dank an Chris und Matt. Ich schätze die Hilfe bei dem Versuch, dies zu lösen, sehr.
Mel

0

Ich habe Lyubomirs Antwort auf diesen Beitrag gefunden. Es klappt. Verschachtelte Routen mit React Router v4 / v5

Der Link zum Menüpunkt lautet:

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

Der Anzeigepfad lautet:

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

Vor dem Namen der Komponente steht ein Doppelpunkt. Nicht sicher warum. Wenn jemand weiß, wie dieser Ansatz heißt, wäre ich für eine Referenz dankbar, damit ich versuchen kann, ihn zu verstehen.


Eigentlich - ich habe bald gesprochen. Während dies funktioniert. Es gibt eine Panne. Es funktioniert nur mit einem Link. Wenn ich eine Route zu einem zweiten Menü-Link (Lernen) hinzufüge, muss ich nach demselben Ansatz die Anzeige für das Konto löschen, um die Lernkomponente rendern zu können. Wenn ich versuche, beide zu behalten, wird die Kontokomponente auch dann gerendert, wenn ich nach der Lernkomponente gefragt habe.
Mel

Verwenden :ist ein paramsPlatzhalter. So /app/accountist das gleiche wie /app/sdfsfjfdjfjfdnfjnsdfjn, was das gleiche ist wie /app/doefmdund so weiter. Die Zeichenfolge wird lediglich einer deklarierten params-Variablen zugewiesen (siehe Beispiel hier: reacttraining.com/react-router/web/example/url-params , das die Zeichenfolge als params deklariert :id). Wenn Sie params verwenden möchten, müssen Sie entweder Regex Wegfindung (für Multi params) oder machen Sie Ihre Pfade mehr einzigartig zu verwenden , im /app/dashboardVergleich zu /app/settings/5e0b97003e95542de2d8e3a1, wo 5e0b97003e95542de2d8e3a1ein einzigartiges params ist id.
Matt Carlotta

Kurz gesagt, /app/:idwürde mit der gleichen Route wie /app/:accountübereinstimmen, die mit der gleichen Route wie übereinstimmen würde /app/settings. Aufgrund des Param-Platzhalters sind sie nicht eindeutig genug, um mit ihnen übereinzustimmen.
Matt Carlotta

@ MattCarlotta - wenn Sie die Punkte wollen - ich gebe sie Ihnen gerne.
Mel

Ich bin ok, danke. Wenn alle Ihre Fragen beantwortet wurden, können Sie das Kopfgeld entfernen.
Matt Carlotta
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.