首页 > 解决方案 > 直接从组件的事件处理程序中返回功能组件

问题描述

我对 React 比较陌生。

我有一个 React 菜单组件,我在其中尝试从 sidenav 使用 React Router。sidenav 中有两个超链接,单击时应重定向到另一个组件。但是,正常的 React 路由不起作用。

作为替代方案,我实现了一个单独的功能组件 Redir(),它显式调用 history.push() 以导航到另一个组件(不能从组件内直接调用 history.push()。)

例如,在 HomelinkClicked() 中,我使用 return 语句来返回功能组件 Redir 的实例,但我怀疑这是否有效。事实上,Redir() 中的警报永远不会到达。

是否可以使用 return 语句直接从组件的事件处理程序中返回功能组件?另外,我不确定使用 JSX 以外的功能组件返回是否安全。


import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { useHistory } from "react-router-dom";

    class MyMenu extends Component {
      constructor() {
          super();
          this.state = {
          };
        this.HomelinkClicked = this.HomelinkClicked.bind(this);
        this.AppjQMPage2linkClicked = this.AppjQMPage2linkClicked.bind(this);
      }

      HomelinkClicked() {
        document.getElementById("mySidenav").style.width = "0";
        document.getElementById("main").style.marginLeft= "0";
        return <Redir target="/AppjQM" />;
      }

      AppjQMPage2linkClicked() {
        document.getElementById("mySidenav").style.width = "0";
        document.getElementById("main").style.marginLeft= "0";
        return <Redir target="/AppjQMPage2" />;
      }

      render() {
          return (
            <div id="mySidenav"  class="sidenav">
              <a href="#" class="closebtn" onClick={this.CloseClicked}>&times;</a>
                <Router>
                  <Link onClick={this.HomelinkClicked} >Home</Link>
                  <Link onClick={this.AppjQMPage2linkClicked} >AppjQMPage2</Link>
                </Router>
            </div>
          );
      }
     }

    function Redir(props) {

      alert('Hook!');
      let history = useHistory();
      history.push(props.target);
    }

标签: javascriptreactjsredirect

解决方案


感谢您的回答。路由不起作用,我尝试过,但发现单击路由链接时嵌套在 AppjQM/AppjQMPage2 中的 MyMenu 存在无限循环,完全冻结 UI。

因此决定完全重写应用程序,有一个仅渲染一次 MyMenu 的顶级应用程序,然后是 AppjQM 或 AppjQMPage2,具体取决于名为 activecomponent 的状态变量的值,可以从组件外部,从 MyMenu,通过使用简单的 onClick 处理程序单击超链接。

这直到现在才起作用。第一个超链接 (Home) 没有问题,但第二个 (AppjQMPage2) 没有显示 AppjQMPage2 的任何内容。它只说:来自 AppjQMPage2 的您好,并且不显示任何标题或任何告诉 AppjQMPage2 呈现的内容:

<JQMHeader />
<JQMPanel />

以下是想要重现的完整代码和 css/html:

// client/src/App.js
import React, { Component, useState } from "react";
import ReactDOM from 'react-dom';
import './hamburger.css';
import './style.css';
import $ from "jquery"; 
import {Link} from 'react-router-dom';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';


class App extends Component {
  constructor() {
    super();
    this.state = {
      activecomponent: 1
    };
  }

  changeState1 = () =>{
    this.setState({ activecomponent: 1 }, function () {
      //alert('State is now: ' + this.state.activecomponent);
    });  
  }

  changeState2 = () =>{
    this.setState({ activecomponent: 2 }, function () {
      //alert('State is now: ' + this.state.activecomponent);
    });  
  }

  render() {

    if (this.state.activecomponent === 1) {
      return (
        <div>
          <MyMenu parentCallback1={this.changeState1} parentCallback2={this.changeState2} />
          <AppjQM />
          <div>Hello from AppjQM</div>
        </div>
      );     
    }
    if (this.state.activecomponent === 2) {
      return (
        <div>
          <MyMenu parentCallback1={this.changeState1} parentCallback2={this.changeState2} />
          <AppjQMPage2 />
          <div>Hello from AppjQMPage2</div>
        </div>
      );     
    }
  }
}

class AppjQM extends Component {
  constructor() {
    super();
    this.state = {
    };
  }

  render() {
    return (
      <div>
        <div data-role="page" id="afsprakenplanner">
          <JQMHeader />
          <JQMPanel />
          <Content />
        </div>
      </div>
    );
  }
}

class AppjQMPage2 extends Component {
  constructor() {
    super();
    this.state = {
    };
  }

  render() {
    return (
      <div>
        <div data-role="page" id="afsprakenplanner">
          <JQMHeader />
          <JQMPanel />
        </div>
      </div>
    );
  }
}

class MyMenu extends Component {
  constructor() {
      super();
      this.state = {

      };
    this.CloseClicked = this.CloseClicked.bind(this);
    this.HomelinkClicked = this.HomelinkClicked.bind(this);
    this.AppjQMPage2linkClicked = this.AppjQMPage2linkClicked.bind(this);
  }

  CloseClicked() {
    document.getElementById("mySidenav").style.width = "0";
    document.getElementById("main").style.marginLeft= "0";
  }

  HomelinkClicked() {
    document.getElementById("mySidenav").style.width = "0";
    document.getElementById("main").style.marginLeft= "0";
    this.props.parentCallback1();
  }

  AppjQMPage2linkClicked() {
    document.getElementById("mySidenav").style.width = "0";
    document.getElementById("main").style.marginLeft= "0";
    this.props.parentCallback2();
  }

  render() {
      return (
        <div id="mySidenav"  class="sidenav">
          <a href="#" class="closebtn" onClick={this.CloseClicked}>&times;</a>
            <a href='#' onClick={this.HomelinkClicked} >Home</a>
            <a href='#' onClick={this.AppjQMPage2linkClicked} >AppjQMPage2</a>
        </div>
      );
  }
}

class JQMHeader extends Component {
    constructor() {
        super();
        this.state = {
        };

    this.HamburgerClicked = this.HamburgerClicked.bind(this);
    }

    HamburgerClicked() {
      document.getElementById("mySidenav").style.width = "440px";
      document.getElementById("main").style.marginLeft = "440px";
    }

    render() {
        return (
        <div data-role="header">
            <header>
                <div id="hamburger" onClick={this.HamburgerClicked}>
                    <div></div>
                    <div></div>
                    <div></div>
                </div>
                <div id="h1"><h1>Afsprakenplanner</h1></div>
            </header>
        </div>
        );
    }
}

class JQMPanel extends Component {
  constructor(props) {
    super(props);
    this.state = {

    }

  }

  render() {

    return (
        <div data-role="panel" class="blackpanel" id="navpanel" data-display="push" data-theme="a" data-position="left">
          <div data-role="controlgroup" data-corners="false">
              <h1>Maak account</h1>
              <div class="smallspacer"></div>
              <div id="panelhint">
                  Vul onderstaande gegevens in, en klik vervolgens op de knop Maak account.
              </div>
              <div class="smallspacer"></div>
              <div class="smallspacer"></div>
              <div class="textcontainer">
                  Achternaam:
              </div>
              <input type="text" id="naampan" />
              <div class="smallspacer"></div>
              <div class="textcontainer">
                  E-mailadres:
              </div>
              <input type="text" id="emailadrespan" />
              <div class="smallspacer"></div>
              <div class="textcontainer">
                  Wachtwoord:
              </div>
              <div class="form-group">
                  <input type="password" id="password" /><span id="toggle" class="fa fa-fw fa-eye pass-icon" onclick="showhide();"></span>
              </div>
              <div class="smallspacer"></div>
              <div class="textcontainer">
                  Telefoonnummer:
              </div>
              <input type="text" id="telefoon" />
              <div class="smallspacer"></div>
              <div class="textcontainer">
                  Adres:
              </div>
              <input type="text" id="adres" />
              <div class="smallspacer"></div>
              <div class="textcontainer">
                  Postcode:
              </div>
              <input type="text" id="postcode" />
              <div class="smallspacer"></div>
              <div class="textcontainer">
                  Woonplaats:
              </div>
              <input type="text" id="woonplaats" />
              <div class="smallspacer"></div>
              <div id="button"><input type="button" onclick="Controleer()" value="MAAK ACCOUNT" /></div>
              <div class="smallspacer"></div>
          </div>
      </div>
    )
  }
}
  


class Content extends Component {
  constructor() {
    super();
    this.state = {

    };
  }

  render() {
    return (
        <div data-role="content" id='main'>
            <div id="contentLayer"></div>

            <div id="content">
                <h1>Afsprakenplanner</h1>
                <form id="theForm">
                    <div id="cont">
                        <div class="stack">
                            <div class="headertext">Naam en e-mailadres:</div>
                            <div id="inputsnaamemail">
                                <input type="text" id="naam" placeholder="Achternaam" data-mini="true" autocomplete="off" />
                                <ul id="namenlijst"></ul>
                                <input type="text" id="emailadres" placeholder="E-mailadres" data-mini="true" />
                            </div>
                            <div id="accfailure"><div id="accfailuretext">Geen account? </div><div id="maakaccbutton"><a data-role="button" data-corners="false" id="maak">Maak</a></div></div>
                        </div>
 
                    </div>
                </form>
            </div>
        </div>
    );
  }
}

export default App;

样式.css:

.stack {
    text-align: left;
    display: inline-block;
    border: 0px solid;
    min-width: 225px;
    max-width: 225px;
    height: 140px;
    color: royalblue;
    vertical-align: middle;
    margin-bottom: 10px;
}

html, body {
    margin:0;
    padding:0;
 }

header {
    height: 30px;
    top: 0;
}

#hamburger {
    float: left;
    margin-left: 10px;
}

#content {
    padding: 22px 10px 10px 10px;
}

.largespacer {
    height: 120px;
}

div[data-role=panel][data-theme=a] {
    background: #3b5998;
    color: white;
    font-family: Arial;
}

.ui-shadow-inset, .ui-corner-all {
    background-color: #fff;
    box-shadow: none;
}

.blackpanel .ui-input-text {
    box-shadow: none;
    background-color: black;
}


#main {
    overflow: hidden;
}

#mySidenav {
    overflow:visible;
    scrollbar-color: #3b5998 #3b5998;
}

#mySidenav::-webkit-scrollbar-button {
    height: 49px;
}

#mySidenav::-webkit-scrollbar {
    background:#3b5998;
}

#mySidenav::-webkit-scrollbar-thumb {
    background: #223868;
}

#mySidenav {
    height: 100%;
    width: 0px;
    position: fixed;
    z-index: 2;
    top: 0;
    left: 0;
    background-color: #7ea0eb;
    color: white;
    overflow-x: hidden;
    padding-top: 60px;
    transition: width .5s;
  }
  
  #mySidenav a {
    padding: 8px 8px 8px 32px;
    text-decoration: none;
    font-size: 18px;
    color: #fff;
    display: block;
  }

  #mySidenav .closebtn {
    position: absolute;
    top: 0;
    right: 25px;
    font-size: 36px;
    margin-left: 50px;
  }
  
  #main {
    transition: margin-left .5s;
  }

汉堡包.css:

header {
    background-color: #3b5998;
    padding: 10px;
    text-decoration: none;
    position: fixed;
    width: 100%;
    z-index: 1;
    -webkit-box-shadow: 0px 3px 7px 0px rgba(0, 0, 0, 0.4);
    box-shadow: 0px 3px 7px 0px rgba(0, 0, 0, 0.4);
}

#h1 {
    font-size: 6px;
    color: white;
    float: left;
    position: fixed; /* or absolute */
    left: 50%;
    transform: translate(-50%);
}

/*
using background color is important to cover the menu
position absolute isset to cover the whole viewport
*/
#content {
    background-color: #FFFFFF;
    padding: 52px 10px 10px 10px;
    position: relative;
    width: auto;
    height: 100%;
    -webkit-box-shadow: -10px 0px 9px 0px rgba(0, 0, 0, 0.4);
    box-shadow: -10px 0px 9px 0px rgba(0, 0, 0, 0.4);
}

/*
the hamburger button with a little gradient effekt
*/
#hamburger {
    border: 1px solid #374C77;
    border-radius: 3px 3px 3px 3px;
    cursor: pointer;
    display: block;
    height: 24px;
    padding: 3px 4px 3px;
    position: relative;
    width: 25px;
    background: #4569b2;
    background: -moz-linear-gradient(top, #4569b2 0%, #4062a5 100%);
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #4569b2), color-stop(100%, #4062a5));
    background: -webkit-linear-gradient(top, #4569b2 0%, #4062a5 100%);
    background: -o-linear-gradient(top, #4569b2 0%, #4062a5 100%);
    background: linear-gradient(to bottom, #4569b2 0%, #4062a5 100%);
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#4569b2', endColorstr='#4062a5', GradientType=0);
}

    /*
The white stripes in the hamburger button
*/
    #hamburger div {
        background-color: #fff;
        border: 1px solid #eee;
        border-radius: 2px 2px 2px 2px;
        height: 2px;
        margin-top: 3px;
        width: 90%;
    }

/*
The Layer that will be layed over the content
so that the content is unclickable while menu is shown
*/
#contentLayer {
    display: none;
    height: 100%;
    overflow-x: hidden;
    overflow-y: auto;
    position: absolute;
    right: 0;
    top: 0;
    width: 30%;
    z-index: 5;
}

索引.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="stylesheet" href="https://code.jquery.com/mobile/1.3.0/jquery.mobile-1.3.0.min.css" />
    <script type="text/javascript" src="https://code.jquery.com/jquery-1.8.2.min.js"></script>
    <script type="text/javascript" src="https://code.jquery.com/mobile/1.3.0/jquery.mobile-1.3.0.min.js"></script>
    <style>
        body {
            font-family: Arial;
            overflow: visible;
        }
    </style>
    <script>
      $(document).ready(function () {
        
        $("#maak").click(function () {
              $("#navpanel").panel('open');
            });
      });
    </script>
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React jQuery Mobile test</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

推荐阅读