react-router-dom link works in app.js(parent component) but doesn't work in child component

Viewed 1378

I have 'Nav' component in 'app.js' ( parent component ) and 'home.js' conditionally to have white space responsively on the bottom of the header only in Homepage by setting header { height: 90vh }.

In order to do that, I included < Nav > in header tag in 'Home.js'.

And Here the problem came up, Nav Link works well in app.js, but it's not even clickable in home.js.

Homepage Header Image

index.js

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter as Router } from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';
import Store from './Store';
import Routes from './Routes';

require('./styles/main.scss');

const root = document.createElement('div');
document.body.appendChild(root);

render(
  <Provider store={Store}>
    <Router history={createHistory()}>
      <Routes />
    </Router>
  </Provider>,
  root,
);

Routes.js

import React from 'react';
import { Switch, Route } from 'react-router-dom';
import { hot } from 'react-hot-loader';
import App from './src/app';
/* --- Components --- */
import Loader from './src/shared/loader';

const Home = Loader({
      loader: () => import('./src/components/Home' /* webpackChunkName: 'Home' */),
    });

const Login = Loader({
      loader: () => import('./src/components/login' /* webpackChunkName: 'Login' */),
    });

const NoMatch = Loader({
      loader: () => import('./src/shared/NoMatch' /* webpackChunkName: 'NoMatch' */),
    });

const routes = () => (
      <div>
        <App />
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/login" component={Login} />
          <Route component={NoMatch} />
        </Switch>
      </div>
    );

const Routes =
      !module.hot || process.env.NODE_ENV === 'production'
        ? routes
        : hot(module)(routes);

export default Routes;

App.js

import React from 'react';
import { Helmet } from 'react-helmet';
/* --- shared --- */
import Loader from './shared/loader';

const Nav = Loader({
  loader: () => import('./shared/nav' /* webpackChunkName: 'Nav' */),
});

const App = props => {
  const isHomepage = window.location.pathname === '/';

  return (
    <div id="app">
      <Helmet>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>###</title>
        <meta property="og:title" content="###" />
        <meta property="og:description" content="###" />
        <meta property="og:type" content="###" />
        <meta property="og:url" content="###" />
        <meta property="og:image" content="###" />
        <link type="text/plain" rel="author" href="http://domain/humans.txt" />
        <link type="text/plain" rel="author" href="http://domain/robots.txt" />
        <link
          href="https://fonts.googleapis.com/css?family=Nanum+Gothic"
          rel="stylesheet"
        />
        <link
          rel="stylesheet"
          href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"
        />
      </Helmet>
      {props.children}
      {!isHomepage ? <Nav /> : null}
    </div>
  );
};

export default App;

nav.js

import React from 'react';
/* --- shared --- */
import * as data from './data';
import Ul from './ul';

const Nav = () => (
  <div className="nav">
    <div className="flex justify-between nav--top">
      <p className="mh-auto f-mini fw3">
        상담전화
        <span className="f-regular"> xxx&#45;xxxx&#45;xxxx</span>
      </p>
      <Ul links={data.redirectToLogin} />
    </div>
    <h1>
      <Ul links={data.redirectToHome} />
    </h1>
    <div className="hr-center">
      <Ul links={data.nav} />
    </div>
  </div>
);

export default Nav;

ul.js

import React from 'react';
import { Link } from 'react-router-dom';

const Ul = ({ links }) => (
  <ul className="nav-menu">
    {links &&
      links.map(e => (
        <li>
          <Link className={e.className} key={e.id} to={e.to}>
            {e.name}
          </Link>
        </li>
      ))}
  </ul>
);

export default Ul;

Home.js

import React from 'react';
/* --- Components --- */
import HomeMain from './Home.main';
/* --- Shared --- */
import Nav from '../shared/nav';

const Home = () => (
  <div>
    <header>
      <Nav />
      <div className="header-text--box">
        <h2>
          <span className="f-regular f-en lh-3">NO MSG!</span>
          <br />
          오늘도 열심히 일하는 당신에게 착한 가격의 집밥을 선물하세요.
        </h2>
      </div>
    </header>
    <HomeMain />
  </div>
);

export default Home;

Version

react: 16.4.2 
react-dom: 16.4.2 
react-redux: 5.0.7 
react-router-dom: 4.3.1 
react-router-redux: 5.0.0-alpha.9

Thank you for taking your time to help me out.

2 Answers

I'll make a simplified example here of setup that I normally use. So, what about this approach?

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import Provider from "react-redux/es/components/Provider";
import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers/rootReducer';
import thunk from 'redux-thunk';

// Usually I would do this in an external file, but here I'm configuring my store

const myStore = configureStore(initialState) {
  return createStore(
    rootReducer,
    initialState,
    applyMiddleware(thunk)
  );
}    

// After that, I'm wrapping my App component inside of the Provider and passing in the configured store

ReactDOM.render((
    <Provider store={myStore }>
        <App/>
    </Provider>
), document.getElementById('root'));

App.js

import React, { Component } from 'react';
import { BrowserRouter } from 'react-router-dom'
import PrimaryLayoutContainerComponent from "../containers/PrimaryLayoutContainer";

// In my App.js I'm wrapping my primary layout container with BrowserRouter to get access to the history and also passing in the props

class App extends Component {
    render() {
        return (
            <BrowserRouter>
                <PrimaryLayoutContainerComponent {...this.props}/>
            </BrowserRouter>
        );
    }
}
export default App;

PrimaryLayoutContainerComponent.js
In this (main container) I would take the full advantage of my previous configs as well as whatever is offered and needed regarding the Redux state management. I also use withRouter at this stage to get access to the history object's properties.

import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as myActions from '../actions';
import PrimaryLayout from "../components/layout/PrimaryLayout";
import { withRouter } from 'react-router';
import PropTypes from 'prop-types';


class PrimaryLayoutContainerComponent extends Component {
    render() {
        return (
            <div>
                 //Here we pass our history as props
                <PrimaryLayout  history={this.props.history} 
                                propsX={this.props.propsX}

                                propsY={this.props.actions.propsY}
                />
            </div>
        )
    }
}

// In these (and all other cases) I would highly recommend using propTypes.
// They could give you the answer in where the issues might be in your future development 

PrimaryLayoutContainerComponent.propTypes = {
    loggedUser: PropTypes.string,
    actions: PropTypes.object.isRequired
};

const mapStateToProps = (state) => {
    return { xxxx }
}

const mapDispatchToProps = (dispatch) => {
    return {
        actions: bindActionCreators(myActions, dispatch)
    };
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(PrimaryLayoutContainerComponent));

...and then finally in my Primary Layout Component I would define all my routes like this:

PrimaryLayoutComponent.js

import React, { Component } from 'react';
import { Switch, Route, Redirect, Link } from 'react-router-dom';
// ... import all my compoenents


class PrimaryLayout extends Component {
    constructor(props) {
        super(props);
        this.state = {
            currentRoute: '' // I would use the state to dynamically change the  
                             // routes depending on which one was clicked
        }
    }



    componentWillReceiveProps(nextProps) {
        this.setState({
            currentRoute: nextProps.history.location.pathname
        })
    }

    componentWillMount() {
        this.setState({
            currentRoute: this.props.history.location.pathname
        })
    }

    render() {

        const { currentRoute } = this.state;

        return (
            <div className="wrapper">
                <header className="xxxx" role="banner">
                    <div className="container">
                        <div className="xxxxx">


                           <Link to="/home">
                             <img src={logo} alt="your logo"/> Your App Name
                           </Link>
                            <ul className="navbar__menu">
                                    <li className={currentRoute === "/home" ? "active" : ""}>
                                        <Link to="/home"> Home </Link>
                                    </li>

                                    <li className={currentRoute === "/componentOne" ? "active" : ""}>
                                        <Link to="/componentOne"> componentOne</Link>
                                    </li>

                                    <li className={currentRoute === "/componentTwo" ? "active" : ""}>
                                        <Link to="/componentTwo"> componentTwo</Link>
                                    </li>
                            </ul>

                        </div>
                    </div>
              </header>

// At the very end, I'm using the Switch HOC and defining my routes inside of a main tag

               <main>
                <Switch>
                      <Route path='/home' component={HomeContainerComponent} />
                  <Route path='/componentOne' component={componentOne} />               
                  <Route path='/componentTwo' component={componentTwo} />
                  <Redirect to="/home"/>
                </Switch>
                </main>
            </div>
        )
    }
}

export default PrimaryLayout;

I hope my approach was helpful to you and if you have any further questions, please let me know. I'll answer them ASAP

Looking at your styling, it seems the header has a negative z-index.

By setting a negative z-index, this is likely to interfere with children of the <header> (ie your <nav> component), from receiving click events and/or responding to the cursor correctly.

I would recommend removing z-index: -1; from your header.scss to address your problem. Hope that helps!

Related