3

I'm building a React app where I do NOT want the URL in the browser to be updated. I am NOT using 'react-router-dom' but only 'react-router' and MemoryRouter (https://reacttraining.com/react-router/web/api/MemoryRouter). The history.push() is available directly in the component statements but I wish to pass the history to children of children of these main components but the property is undefined.

Here is the Router section in main App.js (components Home and ScreeningTool can access this.props.history.push() as expected):

...
import {Route, MemoryRouter} from "react-router";
...
<MemoryRouter>
    <Route exact path="/" component={Home} />
    <Route path="/screening-tool" component={ScreeningTool} />
</MemoryRouter>
... 

Both Home and ScreeningTool both use child component AppLink that generates a 'link' to navigate between Home and ScreeningTool like so (notice I'm passing 'history' as a prop):

Home.js:
...
<AppLink
    url="/"
    label="Screening Tool"
    history={this.props.history}
/>
...

AppLink.js:
...
<div className="pseudo-link">
    <span onClick={() => this.props.history.push(this.props.url)}>
        {this.props.label}
    </span>
</div>
...

The above code works. But there are children components in Home that will create their own AppLinks and also greatgrandchildren. I do NOT want to pass the history property as a component prop from Parent to Child to GrandChild components because this does not seem efficient. I have found the following stackoverflow questions but none of these options are working for me:

this.props.history.push works in some components and not others

react-router getting this.props.location in child components

I tried the newer 'userHistory' as described in the second URL above:

...
import { useHistory } from 'react-router';
...
render() {
    let history = useHistory();
    return (
        <div className="pseudo-link">
            <span onClick={() => history.push(this.props.url)}>
                {this.props.label}
            </span>
        </div>
    );
}
...

but I get Error: Invalid hook call. Hooks can only be called inside of the body of a function component..

I tried using withRouter as defined here https://reacttraining.com/react-router/web/api/withRouter but I get Error: Invariant failed: You should not use <withRouter(PseudoLink) /> outside a <Router>.

Finally, the accepted answer for this.props.history.push works in some components and not others ends with block of code export default withRouter(connect(mapStateToProps, matchDispatchToProps)(ChildView)); but does not explain where mapStateToProps or matchDispatchToProps comes from?

I'm thinking the issue is that I am using MemoryRouter and not normal/most common Router from 'reacto-router-dom'.

Can anyone help me out?

1 Answers1

2

useHistory is a Hook so it should be used in a functional component, not inside a class based component.

Finally, the accepted answer for this.props.history.push works in some components and not others ends with block of code export default withRouter(connect(mapStateToProps, matchDispatchToProps)(ChildView)); but does not explain where mapStateToProps or matchDispatchToProps comes from?

-If you're not using redux then you can just use

export default withRouter(yourComponentName);

update

I've changed the AppLink component to this and it is working fine

import React from "react";
import "./AppLink.css";
import { useHistory } from 'react-router';

const AppLink = props => {
  const history = useHistory();
  return (
    <div
      className="app-link-button"
      onClick={() => history.push(props.url)}
    >
      <span>{props.label}</span>
    </div>
  );
};

export default AppLink;
Ramesh Reddy
  • 10,159
  • 3
  • 17
  • 32
  • I have changed the class component to be functional but get error still at useHistory() 'TypeError: Cannot read property 'history' of undefined' – Pippy Longstocking Apr 04 '20 at 18:00
  • as far as withRouter() I explain above that i already used that and I included the error. – Pippy Longstocking Apr 04 '20 at 18:02
  • @PippyLongstocking Make sure all the elements that use `withRouter` are wrapped with `BrowserRouter` [docs](https://reacttraining.com/react-router/web/api/BrowserRouter) – Ramesh Reddy Apr 04 '20 at 18:20
  • Ramesh, import React from 'react'; import { useHistory } from "react-router"; import './AppLink.scss'; const AppLink = (props) => { let history = useHistory(); console.log('HISTORY:::', history); return (
    history.push(props.url)}> {props.label}
    ); } export default AppLink; Also, I am not using react-router-dom where BrowserRouter seems to come from. I"m only using 'react-router'
    – Pippy Longstocking Apr 04 '20 at 18:39
  • Ramesh, I do not know what you mean. This is a child component to show a label to navigate between the parent compents, and those parent components are wrapped by Router as I poseted code above....can you provide example? – Pippy Longstocking Apr 04 '20 at 18:47
  • Ramesh I think you are confused - I am using MemoryRouter and NOT react-router-dom I do not want to update the URL so NOT using BrowserRouter. Thanks for trying to help. – Pippy Longstocking Apr 04 '20 at 18:55
  • Ramesh, again, if you look at the code in the question above you will see the first sample code already has what you are suggesting: – Pippy Longstocking Apr 04 '20 at 19:57
  • Can you replicate this in stackblitz or codesandbox, I'll look into it. – Ramesh Reddy Apr 04 '20 at 20:01
  • Ramesh, I added it here (i pulled only the code i need from my main app https://stackblitz.com/edit/react-zr95ge but this code seems to work fine no issues with useHistory...I'm stumped. – Pippy Longstocking Apr 04 '20 at 22:07
  • @PippyLongstocking updated my answer. Yeah, it's working as intended. You can mark this as the answer as your issue is solved. – Ramesh Reddy Apr 04 '20 at 22:08