Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Components not re-rendering on route change - React HashRouter

I've got a problem with react and react-router. When I click on a link (in my example contact in Footer.js), the url changes, but the desired component Location is not shown. When I refresh the site then, the correct component is displayed.

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

What I'm doing wrong? Why it is not working, when I click on a link?

like image 213
dns_nx Avatar asked Apr 27 '18 08:04

dns_nx


2 Answers

Got it working now. I needed to change <HashRouter> to <Router>. Then it works fine.

UPDATE: This solution solves the problem, but then there is a different problem: When I have navigated and refresh the page, then an error (404) is thrown, because there is of course no such a page on the server.

I need to get the HashRouter work.

like image 150
dns_nx Avatar answered Sep 19 '22 19:09

dns_nx


When you declare your routes in App.js, you should pass the props to the component:

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

You should stick to the <Router> solution as having unnecessary hash in the url is ugly.

When I have navigated and refresh the page, then an error (404) is thrown, because there is of course no such a page on the server.

To resolve this, you need to set up a redirect to redirect all requests to the base url for the React app to handle (the url displayed will be preserved).

On Netlify, you can create a _redirects file in your public folder with the content:

/*  /index.html  200

On AWS S3, the redirect rules can be set in S3 or CloudFront, see the answers here.

For Google Cloud bucket, see this.

For Github pages, see this.

like image 20
Roy Wang Avatar answered Sep 18 '22 19:09

Roy Wang