【问题标题】:Components not re-rendering on route change - React HashRouter组件不会在路由更改时重新渲染 - React HashRouter
【发布时间】:2018-04-27 08:57:37
【问题描述】:

reactreact-router 有问题。 当我单击链接时(在我的示例中,Footer.js 中的 contact),url 发生了变化,但所需的组件 Location 未显示。然后,当我刷新站点时,会显示正确的组件。

App.js:

import React, { Component } from 'react';
import { BrowserRouter as Router, HashRouter, Route, Link } from 'react-router-dom';
import 'bootstrap/dist/css/bootstrap.css';
import Footer from './Footer.js';
import Navigation from './Navigation.js';
import Background from './Background.js';
import Home from './Home.js';
import Products from './Products.js';
import Industries from './Industries.js';
import Partner from './Partner.js';
import Location from './Location.js';
import MeetUs from './MeetUs.js';
import ScrollUp from './ScrollUp.js';
import Divider from './Divider.js';
import Country from './Country.js';
import Language from './Language.js';
import Waypoint from 'react-waypoint';
import $ from "jquery";

class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      currentLanguage: 'en',
      currentBU: '',
      currentIndustry: '',
      showMainProductGroups: false,
      currentCountry: 'group',
      countryObject: Country['group'],
      contacts: [],
      mainProductGroups: [],
    };
  }

  handleCountryChange() {
  //...
  }

  handleLanguageChange() {
  //...
  }

  handleBUChange() {
  //...
  }

  render() {
    const routes = [
      { 
        path: '/',
        exact: true,
        components: () => 
          <div>
            <Home key="home" currentLanguage={this.state.currentLanguage} />
          </div>,
      },
      { 
        path: '/contact',
        exact: true,
        components: () => <Location key="locations" currentLanguage={this.state.currentLanguage} country={this.state.countryObject} contacts= {this.state.contacts} onCountryChange={this.handleCountryChange.bind(this)} />
      },
    ]
    return (
      <HashRouter>
    <div>
      <Background />
      <div id="wrap">
        <div id="main" className="container clear-top marginBottom50px">
            <div id="content">
              <Navigation key="navBar" currentLanguage={this.state.currentLanguage} onLanguageChange={this.handleLanguageChange.bind(this)} onBUChange={this.handleBUChange.bind(this)} onCountryChange={this.handleCountryChange.bind(this)} />
              {
                routes.map((route, index) => (
                <Route key={index} path={route.path} exact={route.exact} component={route.components} />
              ))
              }
            </div>
        </div>
      </div>
      <Footer key="footer" currentLanguage={this.state.currentLanguage} />
      <ScrollUp key="scrollUp" />
    </div>
  </HashRouter>
    );
  }
}

export default App;

Home.js:

import React, { Component } from 'react';
import $ from "jquery";
import {  Link } from 'react-router-dom';
import {withRouter} from 'react-router';
import Language from './Language.js';
import locations from './locations.jpg';
import locationLegend from './locationLegend.jpg';
require('bootstrap')

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

    render() {
        return (
            <div className="container marginTop50px marginBottom50px area">
                <div className="row">
                    <div className="col-12 text-center animDelay2 fadeInDown animated">
                        <h1>International Distribution of Specialty Chemicals</h1>
                    </div>
                </div>
                <div className="row marginTop25px">
                    <div className="col-12 text-center animDelay2 fadeInUp animated">
                        {Language[this.props.currentLanguage].homeStartText}
                    </div>
                </div>
                <div className="row marginTop25px">
                    <div className="col-12 text-center">
                        <img src={locations} className="img-fluid" alt="Locations" />
                    </div>
                </div>
                <div className="row marginTop25px">
                    <div className="col-12 text-center">
                        <img src={locationLegend} className="img-fluid" alt="Locations" />
                    </div>
                </div>
            </div>
        );
    }
}

export default withRouter(Home);

Location.js:

import React, { Component } from 'react';
import $ from "jquery";
import { Link } from 'react-router-dom';
import Language from './Language.js';
import Country from './Country.js';
import ContactPerson from './ContactPerson.js';
import locations from './locations.png';
import phone from './phoneBlack.svg';
import fax from './faxBlack.svg';
import email from './emailBlack.svg';
import {withRouter} from 'react-router';
require('bootstrap');

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

    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log('Country change:' + this.props.country.key);
        $('#selectCountry').val(this.props.country.key); //name['en']
    }

    onCountryChange() {
        let countryName = this.refs.country.value;
        this.props.onCountryChange(countryName);
    }

    render() {
        return (
            <div className="container marginTop50px marginBottom50px area" id="locations">
                <div className="row">
                    <div className="col-12 text-center">
                        <h2>{Language[this.props.currentLanguage].locations}</h2>
                    </div>
                </div>
                <div className="row marginTop25px">
                    <div className="col-12 text-center">
                        <div className="form-group">
                            <select id="selectCountry" className="form-control" ref="country" onChange={this.onCountryChange.bind(this)}>
                                <option defaultValue>{Language[this.props.currentLanguage].selectLocation.toUpperCase()}</option>
                                {
                                    Object.keys(Country).map((countryKey) => {
                                        const country = Country[countryKey];
                                        return (
                                            <option value={countryKey} key={"loc" + countryKey}>{country.name[this.props.currentLanguage].toUpperCase()}</option>
                                        );
                                    })
                                }
                            </select>
                        </div>
                    </div>
                </div>
                <div className="row marginTop25px">
                    <div className="col-12 text-center">
                        {this.props.country.name[this.props.currentLanguage].toUpperCase()}
                        <br />
                        <address>
                            <span dangerouslySetInnerHTML={{__html: this.props.country.address}}></span>
                            <br />
                            <br />
                            <img src={phone} alt="Anrufen" className="phoneMain"></img><span> </span>
                            <a href={this.props.country.phoneHTML}>{this.props.country.phone}</a>
                            <br />
                            <img src={fax} alt="Fax" className="phoneMain"></img><span> </span>
                            <a href={this.props.country.faxHTML}>{this.props.country.fax}</a>
                            <br />
                            <img src={email} alt="Email" className="emailMain"></img><span> </span>
                            <a href={"mailto://" + this.props.country.email}>{this.props.country.email}</a>
                        </address>
                    </div>
                </div>
                <div className="row marginTop25px">
                    <div className="col-12 text-center">
                        {Language[this.props.currentLanguage].vatRegistrationNumber + ": " + this.props.country.vatNo}
                        <br />
                        {Language[this.props.currentLanguage].registrationOffice + ": "}
                        <span dangerouslySetInnerHTML={{__html: this.props.country.registrationOffice}}></span>
                    </div>
                </div>
                <div className="row marginTop50px">
                    <div className="col-12 text-center">
                        <h3>{Language[this.props.currentLanguage].contact}</h3>
                    </div>
                </div>
                <div className="row">

                        {
                            this.props.contacts.map((contact) => {
                                return (
                                    <div className="col-12 col-sm-12 col-md-12 col-lg-6 text-center">
                                        <ContactPerson contact={contact} key={"contact" + contact.id} />
                                    </div>
                                );
                            })
                        }
                </div>
            </div>
        );
    }
}

export default withRouter(Location);

Footer.js:

import React, { Component } from 'react';
import $ from "jquery";
import {  Link } from 'react-router-dom';
import {withRouter} from 'react-router';
import Language from './Language.js';
import phone from './phoneWhite.svg';
import fax from './faxWhite.svg';
require('bootstrap');

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

    render() {
        return (
            <footer className="footer">
                <div className="container-fluid borderTop1px footerLayout">
                    <div className="row">
                        <div className="col-3">
                            <address>
                                <small>
                                    Some text
                                </small>
                            </address>
                        </div>
                        <div className="col-6 text-center">
                            <div className="row">
                                <div className="col-12 col-sm-12 col-md-12 col-lg-3 text-center">
                                    <a href="https://download.group.com" className="nav-link footerLink" target="_self"><small>{Language[this.props.currentLanguage].download}</small></a>
                                </div>
                                <div className="col-12 col-sm-12 col-md-12 col-lg-3 text-center">
                                    <Link to="/imprint" className="nav-link footerLink"><small>{Language[this.props.currentLanguage].imprint}</small></Link>
                                </div>
                                <div className="col-12 col-sm-12 col-md-12 col-lg-3 text-center">
                                    <Link to="/contact" className="nav-link footerLink"><small>{Language[this.props.currentLanguage].contact}</small></Link>
                                </div>
                                <div className="col-12 col-sm-12 col-md-12 col-lg-3 text-center">
                                    <Link to="/termsAndConditions" className="nav-link footerLink"><small>{Language[this.props.currentLanguage].termsAndConditions}</small></Link>
                                </div>
                            </div>
                        </div>
                        <div className="col-3">
                            <ul className="list-inline">
                                <li>
                                    <img src={phone} alt="Anrufen" className="phone"></img> <small><a className="footerLink" href="tel:+49">+49</a></small>
                                </li>
                                <li>
                                    <img src={fax} alt="Fax" className="phone"></img> <small><a className="footerLink" href="tel:+49">+49</a></small>
                                </li>
                            </ul>
                        </div>
                    </div>
                </div>
            </footer>
        );
    }
}

export default withRouter(Footer);

我做错了什么?为什么当我点击链接时它不起作用?

【问题讨论】:

  • 为什么你的代码有多个HashRouters?
  • 我只用一个HashRouter 更改了我的代码。请看我的更新。还是不行。
  • 看看这个,它可能对你有帮助:stackoverflow.com/questions/50208165/…

标签: javascript reactjs react-router react-router-v4


【解决方案1】:

现在可以使用了。 我需要将&lt;HashRouter&gt; 更改为&lt;Router&gt;。然后就可以正常使用了。

更新: 这个解决方案解决了问题,但是出现了一个不同的问题:当我导航并刷新页面时,会抛出错误(404),因为服务器上当然没有这样的页面。

我需要得到HashRouter 的工作。

【讨论】:

  • 您如何部署您的网站?例如。 AWS S3、Netlify、Github 页面?
  • 我假设它只是一个由 apache 托管的标准网络服务器。但是当我在我的开发系统上使用npmnode.js 开发网页时,该功能甚至不存在。
【解决方案2】:

当你在 App.js 中声明你的路由时,你应该将 props 传递给组件:

components: props => <Location {...props} <insert other props> />

您应该坚持使用&lt;Router&gt; 解决方案,因为网址中包含不必要的哈希是丑陋的。

当我导航并刷新页面时,会抛出错误(404),因为服务器上当然没有这样的页面。

要解决此问题,您需要设置重定向以将所有请求重定向到 React 应用程序处理的基本 url(显示的 url 将被保留)。

Netlify 上,您可以在您的公共文件夹中创建一个_redirects 文件,其内容为:

/*  /index.html  200

在 AWS S3 上,可以在 S3 或 CloudFront 中设置重定向规则,请参阅the answers here

对于 Google Cloud 存储桶,请参阅this

有关 Github 页面,请参阅this

【讨论】:

  • 请看我上面的评论。带有 apache 的标准 Web 服务器。我不明白“你应该将道具传递给组件”到底是什么意思。我就是这样做的,不是吗?请查看App.js 代码。
  • 在您的App.js 中,您有components: () =&gt; &lt;Location ... /&gt;。您没有定义 props 参数并将其传递给 Location 组件,就像我在上面所做的那样。
  • 请参阅askubuntu.com/questions/484986/…sej-ko.dk/2017/03/29/…,了解如何将所有路由重定向到index.html
  • 您能否尝试将其托管在另一个平台(Netlify 等)上,看看您的 Apache 重定向配置是否存在问题?
  • 但它也不适用于开发中的npmnode.js
【解决方案3】:

在您的 Route 组件中,您使用 component prop 来传递 Location 组件(而不是 Route 上可用的 render 或 children props),路由器使用 React.createElement 从给定组件创建一个新的 React 元素。这意味着如果您为组件属性提供内联函数,您将在每次渲染时创建一个新组件。这会导致现有组件卸载和新组件安装,而不是仅更新现有组件。使用内联函数进行内联渲染时,请使用 render 或 children 道具。但是,在您的情况下,您似乎无缘无故地使用它,因此您应该只传递组件而不是像这样返回它的内联函数:

const routes = [
      { 
        path: '/',
        exact: true,
        components: <Home key="home" currentLanguage={this.state.currentLanguage}/>

      },
      { 
        path: '/contact',
        exact: true,
        components: <Location key="locations" currentLanguage={this.state.currentLanguage} country={this.state.countryObject} contacts= {this.state.contacts} onCountryChange={this.handleCountryChange.bind(this)} />
      },
    ]

【讨论】:

  • 你的解释很有道理。但是,每条路线我需要几个组件。我上面的代码被简化了。例如,我有一个包含多个组件的主路由。没有内联函数如何实现?
  • 我已经尝试过您的解决方案,但它也不起作用。 url 改变了,但组件没有改变。
【解决方案4】:

让你的路线使用组件如下

import {IndexRoute, Route} from 'react-router';

 <Route component={App}>
    <Route path='/locations' component={LocationComponent}/>
 </Route>

这是我在当前项目中所做的,不使用 HashRouter。

目前,当你这样做时

<Route key={index} path={route.path} exact={route.exact} component={route.components} />

我不认为{route.components} 将其视为一个组件。

【讨论】:

  • 问题是它在没有HashRouter 的情况下工作。正如我所写,它适用于标准Router。但后来我面临其他问题。
  • @dns_nx,你不能在一些jsxts 文件中分离出你的组件吗?这样你就不会得到 404
【解决方案5】:

withRouter() 可能有问题。

你见过吗? https://github.com/ReactTraining/react-router/issues/5037

【讨论】:

猜你喜欢
  • 2020-01-02
  • 2019-06-04
  • 1970-01-01
  • 2021-08-13
  • 2019-06-23
  • 1970-01-01
  • 2019-04-28
  • 2018-03-17
相关资源
最近更新 更多