web-dev-qa-db-fra.com

React Router with - Ant Design Sider: comment remplir la section de contenu avec des composants pour l'élément de menu pertinent

J'essaye d'utiliser le menu latéral AntD comme un panneau d'onglets.

Je souhaite placer des composants à l'intérieur du contenu afin que le panneau de contenu rende le composant associé lorsqu'un élément de menu est cliqué.

Comment faire pour que cette structure prenne des composants comme contenu de chaque élément de menu?

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;

Lorsque j'essaie de rendre cela, aucune erreur n'est générée, mais le contenu div ne se met pas à jour avec le PasswordForgetForm que j'ai spécifié comme contenu de l'élément de menu.

J'ai essayé la suggestion de Chris ci-dessous - qui fonctionne bien pour rendre le composant pour chacune des différentes divs de contenu des éléments de menu dans le segment de mise en page, cependant - l'inconvénient de cette approche est qu'à chaque actualisation de page, le chemin va au composant de contenu pour cet élément de menu particulier, au lieu de la page de test qui contient le menu - et le composant de contenu avec ce contenu pertinent. Si cette approche est jugée judicieuse, existe-t-il un moyen de garder la page actualisée sur la page d'origine, plutôt que d'essayer d'accéder à l'URL d'un élément de menu de sous-ensemble?

Mise à jour

J'ai trouvé ceci littérature . Je pense que c'est peut-être ce que j'ai besoin de savoir, mais le langage est trop technique pour que je le comprenne. Quelqu'un peut-il aider avec une version anglaise simple de ce sujet?

J'ai également trouvé ce tutoriel qui utilise Pose pour aider au rendu. Bien que cette structure soit ce que j'essaie de réaliser, il semble que Pose soit obsolète. Est-ce que quelqu'un sait s'il est nécessaire d'aller au-delà de react-router pour obtenir ce résultat, ou existe-t-il une solution au sein de react que je peux envisager de mettre en œuvre?

Ceci exemple est similaire à ce que je veux, mais je ne trouve rien qui définit la barre latérale (ce qui semble être un argument nécessaire pour que cela fonctionne).

J'ai aussi vu ce post . Bien que certaines des réponses soient un copier-coller de la documentation, je me demande si la réponse d'Adam Gering est plus proche de ce dont j'ai besoin. J'essaie de garder le menu Sider sur la route/dash à tout moment. Cette URL ne doit pas changer - quel que soit l'élément de menu du sider sur lequel l'utilisateur clique, MAIS la division de contenu dans le composant/dash doit être mise à jour pour rendre le composant au chemin de route que j'ai spécifié.

APPLIQUER LA SUGGESTION DE CHRIS

Chris a gentiment offert une suggestion ci-dessous. Je l'ai essayé, mais la circonstance dans laquelle il ne fonctionne pas comme souhaité est lorsque l'élément de menu est cliqué (et le composant concerné se charge correctement dans le contenu div, mais si j'actualise la page, l'actualisation essaie de charger une page avec un url correspondant au composant censé se trouver dans la division de contenu sur la page du tableau de bord.

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;

Dans mon fichier de routes, j'ai:

export const ACCOUNT = '/account';

J'ai également trouvé ce didacticiel - qui utilise la même approche décrite ci-dessus - et qui n'est pas redirigé de la même manière que mon code lors de l'actualisation de la page. Le didacticiel utilise Route au lieu de BrowserRouter - mais sinon, je ne vois aucune différence.

TENTATIVE SUIVANTE

J'ai vu ceci post (merci Matt). J'ai essayé de suivre les suggestions de cet article.

J'ai supprimé le wrapper externe du routeur de la page Tableau de bord (il y a un routeur autour de App.js, où la route vers le tableau de bord est configurée).

Ensuite, dans mon tableau de bord, j'ai changé la division de contenu en:

J'ai ajouté:

let match = useRouteMatch();

à la méthode de rendu dans mon composant Dashboard (comme indiqué ici ).

J'ai ajouté useRouteMatch à ma déclaration d'importation depuis react-router-dom.

Cela produit une erreur qui dit:

Erreur: appel de hook non valide. Les hooks ne peuvent être appelés qu'à l'intérieur du corps d'un composant de fonction. Cela peut se produire pour l'une des raisons suivantes: 1. Vous pouvez avoir des versions incompatibles de React et du moteur de rendu (comme React DOM)) 2. Vous pouvez enfreindre les règles des crochets

Il y a plus de message dans l'erreur ci-dessus mais le débordement de pile ne me permettra pas de le poster car il a un lien court

Je ne sais pas ce que j'ai fait de mal dans ces étapes, mais si je commente l'instruction let, la page se charge.

Quand je vais à/Dashboard et clique sur "compte", je m'attends à ce que le composant de compte soit rendu à l'intérieur du div de contenu que j'ai dans la mise en page sur la page du tableau de bord. Au lieu de cela, il redirige directement vers une page sur localhost3000/account (pour laquelle il n'y a pas de référence de page - c'est juste un composant à rendre dans la page du tableau de bord).

C'est donc pire que le problème avec lequel j'ai commencé - car au moins au début, la redirection ne s'est produite que lors de l'actualisation de la page. Maintenant, cela se produit immédiatement.

J'ai trouvé ce dépôt qui a des exemples de chaque type de route. Je ne vois pas ce que cela fait que je ne suis pas.

Ceci post semble avoir eu le même problème que moi, et l'a résolu en utilisant la même approche que j'ai essayée. C'est un article de 2018, donc le passage du temps peut rendre l'approche obsolète - mais je ne vois aucune différence entre ce que cette solution de publication implémente et ma tentative initiale.

TENTATIVE SUIVANTE

J'avais pensé avoir trouvé une approche qui fonctionne dans la réponse de Lyubomir sur ce post .

J'ai:

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

{${this.props.match.url}/account}> Paramètres du compte

Ensuite, dans le div sur le composant de tableau de bord où je veux afficher ce composant, j'ai:

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

Cela marche. Lors de l'actualisation de la page, je peux continuer à faire fonctionner les choses comme elles le devraient.

TOUTEFOIS, lorsque j'ajoute un deuxième élément de menu avec:

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

Au lieu de rendre le composant d'apprentissage lorsque l'élément de menu pour/learn est cliqué (et que la barre d'URL change pour apprendre), le composant de compte est rendu. Si je supprime l'affichage du compte de la déclaration de changement, je peux afficher le composant d'apprentissage correct.

Avec cette tentative, les éléments de menu fonctionnent pour correspondre à une extension d'URL du tableau de bord (c'est-à-dire localhost/dash/account ou localhost/dash/learn) pour [~ # ~] one [~ # ~ ] élément de menu uniquement. Si j'ajoute un deuxième élément de menu, la seule façon de rendre correctement le 2ème composant est de supprimer le premier élément de menu. J'utilise le commutateur avec des chemins exacts.

Je souhaite que cette solution fonctionne avec plusieurs éléments de menu.

J'ai essayé un chemin alterné pour l'url (par exemple:

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

est le même que:

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

J'ai lu l'explication dans ce blog et même si je ne comprends pas entièrement ces options. J'ai lire ceci . Cela suggère que l'instruction match est maintenant une méthode héritée pour le rendu des composants (maintenant que les hooks sont disponibles). Je ne trouve aucun matériel de formation qui montre comment utiliser les crochets pour atteindre le résultat escompté.

8
Mel

J'ai trouvé la réponse de Lyubomir sur ce post. Ça marche. Routes imbriquées avec react router v4/v5

Le lien de l'élément de menu est:

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

Le chemin d'affichage est:

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

Il y a deux points avant le nom du composant. Pas certain de pourquoi. Si quelqu'un sait comment s'appelle cette approche, je serais reconnaissant de lui donner une référence afin que je puisse essayer de la comprendre.

0
Mel