React Router with-Ant Design Sider:コンテンツセクションに関連するメニュー項目のコンポーネントを追加する方法


8

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のコンポーネントをレンダリングするのにうまく機能しますが、このアプローチの欠点は、ページを更新するたびに、パスがメニューがあるテストページではなく、その特定のメニュー項目、およびその関連コンテンツを持つコンテンツコンポーネント。このアプローチが賢明であると承認されている場合、サブセットメニューアイテムのURLに移動するのではなく、元のページでページを更新し続ける方法はありますか?

更新

この文学を見つけた。これは私が知る必要があるかもしれないと思いますが、言語は私には理解するにはあまりにも専門的です。誰でもこの主題の簡単な英語版を手伝ってくれる?

また、ポーズを使用してレンダリングを支援するこのチュートリアルを見つけました。この構造は私が達成しようとしているものですが、ポーズは廃止されているようです。この結果を得るためにreact-routerを超える必要があるかどうか誰かが知っていますか、またはreact内に実装するように見える解決策がありますか?

このは私が欲しいものに似ていますが、サイドバーを定義するものは何も見つかりません(これは、この機能を動作させるために必要な引数のようです)。

私もこの記事をました。回答の一部はドキュメントのコピーと貼り付けですが、Adam Geringの回答が必要なものに近いのではないかと思います。私は常に/ dashルートにSiderメニューを維持しようとしています。その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;

私のroutesファイルには、次のものがあります:

export const ACCOUNT = '/account';

私はこのチュートリアルも見つけました-これは上記で概説したのと同じアプローチを使用しており、ページの更新時にコードが行うのと同じ方法でリダイレクトされません。このチュートリアルではBrowserRouterの代わりにRouteを使用していますが、それ以外の点では違いがわかりません。

次の試み

私はこの投稿を見ました(Mattに感謝します)。私はその投稿の提案に従うようにしました。

ダッシュボードページから外部ルーターラッパーを削除しました(ダッシュボードへのルートが設定されているApp.jsの周りにルーターがあります)。

次に、ダッシュボードでコンテンツdivを次のように変更しました。

追加した:

let match = useRouteMatch();

ここに示すように、ダッシュボードコンポーネント内のrenderメソッドに追加します。

react-router-domからのimportステートメントにuseRouteMatchを追加しました。

これにより、次のようなエラーが発生します。

エラー:無効なフック呼び出し。フックは、関数コンポーネントの本体の内部でのみ呼び出すことができます。これは、次のいずれかの理由で発生する可能性があります。1. Reactとレンダラー(React DOMなど)のバージョンが一致していない可能性があります。2.フックのルールに違反している可能性があります。

上記のエラーにはより多くのメッセージがありますが、短いリンクがあるため、スタックオーバーフローでは投稿できません

これらの手順で何が間違っていたかはわかりませんが、letステートメントをコメントアウトすると、ページが読み込まれます。

/ Dashboardに移動して[アカウント]をクリックすると、ダッシュボードページのレイアウトにあるコンテンツdiv内にアカウントコンポーネントが表示されることを期待しています。代わりに、localhost3000 / accountのページに直接リダイレクトします(ページ参照はありません-これは、ダッシュボードページ内にレンダリングするコンポーネントにすぎません)。

ですから、これは私が始めた問題よりも悪いです-少なくとも最初は、リダイレクトはページの更新時にのみ発生したためです。今ではすぐに起こります。

さまざまなルートの例が含まれているこのレポを見つけました。私がしていないことが何をしているのか分かりません。

この投稿は私と同じ問題を抱えていたようで、私が試したのと同じ方法で解決しました。2018年からの投稿なので、時間が経つとアプローチが古くなるかもしれませんが、その投稿ソリューションの実装と私の最初の試みとの違いはわかりません。

次の試み

私はこの投稿に対するリュボミールの答えで機能するアプローチを見つけたと思っていました。

私が持っています:

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

{ ${this.props.match.url}/account}>アカウント設定

次に、私が持っているこのコンポーネントを表示したいダッシュコンポーネントのdivで:

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

これは機能します。ページを更新すると、正常に機能するようになります。

しかし、次のように2番目のメニュー項目を追加すると、

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

/ learnのメニュー項目をクリックすると(およびURLバーが学習に変化する)、ラーニングコンポーネントがレンダリングされる代わりに、アカウントコンポーネントがレンダリングされます。switchステートメントからアカウントの表示を削除すると、正しい学習コンポーネントを表示できます。

この試みで、私はのために(つまり、ローカルホスト/ダッシュ/アカウントまたはローカルホスト/ダッシュ/学ぶ)ダッシュボードのURLの拡張子に一致するように取り組んでメニュー項目を持っているONEのみのメニュー項目。2番目のメニュー項目を追加した場合、2番目のコンポーネントを正しくレンダリングする唯一の方法は、最初のメニュー項目を削除することです。正確なパスを持つスイッチを使用しています。

このソリューションで複数のメニュー項目を処理したい。

私はURLのパスを変更しようとしました(例:

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

と同じです:

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

私はこのブログの説明を読みましたが、これらのオプションを完全には理解していません。私はこれ読みました。これは、matchステートメントがコンポーネントをレンダリングするためのレガシーメソッドであることを示しています(フックが利用可能になりました)。期待される結果を達成するためにフックを使用する方法を示すトレーニング資料が見つかりません。


PasswordForgottenFormコンポーネントも含めてください。
AleksandrH

これは、フォームを含む単なるconstコンポーネントです。各メニュー項目には、テストコンポーネントのレイアウトセクションのコンテンツdivにレンダリングされる個別のコンポーネント(一部はクラスコンポーネント)があります。
Mel

回答:


4

より良い解決策は、React Router <Link>を使用して各メニュー項目を特定のパスにリンクさせ、コンテンツ内で<Switch>対応するコンポーネントのレンダリングに使用することです。これがドキュメントです:React router

Reactルーターでレンダリング

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

メニューキーでレンダリング

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

antd-menu-click-renderingを編集する


コメントは詳細な議論のためのものではありません。この会話はチャットに移動しました
サミュエルLiew

クリスとマットの両方に感謝します。これを解決しようとするすべての助けにとても感謝しています。
メル

0

この投稿でリュボミールの答えを見つけました。できます。反応ルーター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} />

コンポーネントの名前の前にコロンがあります。なぜだかわかりません。誰かがこのアプローチが何と呼ばれているのかを知っている場合-私はそれを理解しようとすることができるように、リファレンスに感謝します。


実際に-私はすぐに話しました。これは機能しますが。グリッチがあります。1つのリンクでのみ機能します。ルートを2番目のメニューリンク(ラーニングと呼ばれる)に追加する場合、同じアプローチに従って、ラーニングコンポーネントをレンダリングできるようにするために、アカウントの表示を削除する必要があります。両方を保持しようとすると、ラーニングコンポーネントを要求した場合でも、アカウントコンポーネントがレンダリングされます。
Mel

使用:paramsワイルドカードです。だから、/app/account同じである/app/sdfsfjfdjfjfdnfjnsdfjnと同じである、/app/doefmdというように。これは、宣言されたparams変数に文字列を割り当てるだけです-次の例を参照してください:reacttraining.com/react-router/web/example/url-paramsは、文字列をparamsとして宣言します:id)。あなたがのparamsを使用する場合は、どちらか(複数のparamsのための)正規表現パス機能を使用するか、あなたのパスがより一意にする必要があります/app/dashboard/app/settings/5e0b97003e95542de2d8e3a1ところ、5e0b97003e95542de2d8e3a1独特のparamsですid
Matt Carlotta

つまり、/app/:idと同じルートに/app/:account一致し、と同じルートに一致し/app/settingsます。paramワイルドカードのため、照合するのに十分一意ではありません。
Matt Carlotta

@MattCarlotta-ポイントが必要な場合-差し上げます。
Mel

大丈夫、ありがとう。すべての質問に回答した場合は、賞金を削除できます。
Matt Carlotta、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.