My react onClick navigate shows URL, but does not render component - reactjs

I am new to React router here and I am trying to make clicking on a recipe in my 'BrowseRecipes' page redirect to a page dedicated to that recipe. However, when I click on the recipe, the URL shows the correct URL /browse/${recipeID}, but the page I assign to this route does not render. Only the /browse page with a list of all the recipes renders. Does anyone know why?
Here is my APP.js
import AddNewRecipe from './components/AddNewRecipe'
import BrowseRecipes from './components/BrowseRecipes'
import { currentState } from './components/redux';
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Switch, Route, Routes, Link, useParams} from "react-router-dom";
import AuthReqPage from "./components/AuthReqPage"
import Navbar from "./components/Navbar"
import RecipePage from "./components/BrowseRecipes/RecipePage"
import PageNotFound from "./components/PageNotFound"
function App(props) {
return (
<Router>
<div className="App">
<Navbar />
<Routes>
<Route path='/add' element={<AddNewRecipe />} />
<Route path='/' element={<BrowseRecipes />} />
<Route path='/browse' element={<BrowseRecipes />}>
<Route path=':recipeID' element={<RecipePage />}/>
</Route>
<Route path='/authrequired' element={<AuthReqPage />} />
<Route path='/*' element={<PageNotFound />} />
</Routes>
</div>
</Router>
);
}
export default App;
Here is my BrowseRecipe component/page:
export function BrowseRecipes (props){
console.log('browseRecipe running')
let navigate = useNavigate()
let params=useParams()
console.log(params.recipeID)
if(props.recipeStore.length>0)
{
var displayRecipes = props.recipeStore.map(
elem=>
{
return (<li key={elem.recipeID} className='recipeDisplayBox' onClick={()=>navigate(`/browse/${elem.recipeID}`)}>
{elem.title},
Prep: {elem.prepTime.numeral} {elem.prepTime.unit}
</li>)
}
)
}
return(
<div>
<h1>Browse Recipes</h1>
<h2>Your recipes:</h2>
<ul>
{displayRecipes}
</ul>
</div>
)
}
const mapStateToProps=(state)=>{
return {recipeStore: state.recipe}}
export default connect(mapStateToProps)(RequireAuth(BrowseRecipes))
And here is the individual recipe page that failed to render:
export function RecipePage (props){
console.log('RecipePage running')
let params=useParams()
return(
<div>
<h1>{params.recipeID}</h1>
</div>
)
}
const mapStateToProps=(state)=>{
return {recipeStore: state.recipe}}
export default connect(mapStateToProps)(RequireAuth(RecipePage))
"RequireAuth" here is a higher-order component that redirects the page to 'Please Sign In' page if the user is not signed in.
Did I misunderstand something about the use of UseParams? Please help me shed some light! Thank you very much

You've rendered the RecipePage component on a nested route from the "/browse" route rendering the BrowseRecipes component.
<Route path='/browse' element={<BrowseRecipes />}>
<Route path=':recipeID' element={<RecipePage />}/>
</Route>
In this configuration the BrowseRecipes is required to render an Outlet component for the nested routes to be rendered into.
Example:
import { Outlet, useNavigate, useParams } from 'react-router-dom';
export function BrowseRecipes (props) {
const navigate = useNavigate();
const params = useParams();
let displayRecipes;
if (props.recipeStore.length) {
displayRecipes = props.recipeStore.map(elem => {
return (
<li
key={elem.recipeID}
className='recipeDisplayBox'
onClick={() => navigate(`/browse/${elem.recipeID}`)}
>
{elem.title},
Prep: {elem.prepTime.numeral} {elem.prepTime.unit}
</li>
);
});
}
return (
<div>
<h1>Browse Recipes</h1>
<h2>Your recipes:</h2>
<ul>
{displayRecipes}
</ul>
<Outlet /> // <-- nested routes render here
</div>
);
}
If you don't want to render both BrowseRecipes and RecipePage at the same time, then create a nested index route specifically for BrowseRecipes.
Example:
<Route path='/browse'>
<Route index element={<BrowseRecipes />} /> // <-- "/browse"
<Route path=':recipeID' element={<RecipePage />} /> // <-- "/browse/:recipeID"
</Route>
For more information, see:
Index Routes
Layout Routes

Related

Adding routes inside a component in react router dom version 6

I am trying to migrate from react router 5 to react router 6. I coded a simple frontend where only if the login button is clicked you can go to the profile page. and inside profile page there are two links to view profile and edit profile.
//App.js
import './App.css';
import Header from './components/Header';
import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';
import Profile from './pages/Profile';
import NotFoundPage from './pages/NotFoundPage';
import Post from './pages/Post';
import GroupProfile from './pages/GroupProfile';
import {BrowserRouter as Router, Routes, Route, Navigate} from 'react-router-dom';
import {useState} from 'react';
function App(){
const [login, setLogin] = useState(false);
const [glogin, setGlogin] = useState(false);
return(
<div className="App">
<Router>
<Header />
<button onClick={() => setLogin(!login)}> {!login?"Login":"Logout"} </button>
<span> | </span>
<button onClick={() => setGlogin(!glogin)}> {!glogin?"GroupLogin":"GroupLogout"} </button>
<Routes>
<Route path='/' element={<HomePage />} />
<Route path='/about' element={<AboutPage />} />
{/*<Route path='/prof'>
{login ? <Profile />:<Navigate to='/' />} Nope, in react_router_dom6 putting other things rather than function that returns pages are not valid
</Route>*/}
<Route path='/group' element={<GroupProfile g_login={glogin} />} />
<Route path='/prof/*' element={<Profile l_ogin={login} />} />
<Route path='/post/:id' element={<Post />} />
<Route path='*' element={<NotFoundPage />} />
</Routes>
</Router>
</div>
)
}
export default App;
And these are groupprofile page and profile page.
GroupProfile.js
import React from 'react';
import {useEffect} from 'react';
import {useNavigate} from 'react-router-dom';
function GroupProfile({ g_login }){
let nav = useNavigate();
useEffect(() => {
if (!g_login) {
nav("/");
}
},[g_login, nav]) //you know useEffect hook, if dependancy list is given, when they change then hook will be triggered.
return(
<div>
<p>This is the GroupProfile</p>
</div>
)
}
export default GroupProfile;
Profile.js
import React, {useEffect} from 'react';
import {Route, Routes, useRouteMatch, Link, useNavigate, useLocation} from 'react-router-dom';
import EditProfile from '../components/EditProfile';
import ViewProfile from '../components/ViewProfile';
/*
function Profile(){
const { path, url } = useRouteMatch();
//console.log(useRouteMatch());
return(
<>
<h3>Profile Page</h3>
<ul>
<li><Link to={`${url}/viewprofile`}>View Profile</Link></li>
<li><Link to={`${url}/editprofile`}>Edit Profile</Link></li>
</ul>
<Route path={`${path}/viewprofile`} component={ViewProfile} />
<Route path={`${path}/editprofile`} component={EditProfile} />
</>
)
}
*/
function Profile({l_ogin}){
let nav = useNavigate();
let location = useLocation();
//console.log(location)
useEffect(() => {
if (!l_ogin) {
nav("/");
}
},[l_ogin, nav]) //you know useEffect hook, if dependancy list is given, when they change then hook will be triggered.
return(
<div>
<h1>Congrats! you made it to the profile page mate...</h1>
<ul>
<li><Link to={`${location}/viewprofile`}>View Profile</Link></li>
<li><Link to={`${location}/editprofile`}>Edit Profile</Link></li>
</ul>
<Routes>
<Route path={`${location}/viewprofile`} element={<ViewProfile />} />
<Route path={`${location}/Editprofile`} element={<EditProfile />} />
</Routes>
</div>
)
}
export default Profile;
I have commented the Profile component that works in react router version 5 and it is supposed to navigate to /prof/viewprofile when View Profile link is clicked. ViewProfile.js and EditProfile.js are simple components
ViewProfile.js
import React from "react";
const ViewProfile = () => {
return(
<div>
<h3><i>This is what you have to see in the profile.</i></h3>
</div>
)
}
export default ViewProfile;
The code I used in react router 5 works fine and when I clicked View Profile it navigates to /prof/viewprofile without any problem. But when I use react router 6 it doesn't navigate to /prof/viewprofile and it just stays in the /prof, but url changes to something like /prof/[object%20Object]/viewprofile. Can someone show me what am I doing wrong here?
location is an object, and if this is what you were trying to use you'd need location.pathname, but this is not what you want to try and use to build paths for descendent routes. When rendering Routes components and descendent routes, the routes are already built relative to the parent route. There is no need to try and build/implement the relative-ness yourself.
Example:
function Profile({ l_ogin }){
const navigate = useNavigate();
useEffect(() => {
if (!l_ogin) {
navigate("/", { replace: true });
}
}, [l_ogin, navigate]);
return(
<div>
<h1>Congrats! you made it to the profile page mate...</h1>
<ul>
<li><Link to="viewprofile">View Profile</Link></li>
<li><Link to="editprofile">Edit Profile</Link></li>
</ul>
<Routes>
<Route path="viewprofile" element={<ViewProfile />} />
<Route path="editprofile" element={<EditProfile />} />
</Routes>
</div>
);
}
If you wanted to keep the first route implementation you were trying to use then this is the correct syntax. You still need to render the routed content on the element prop.
<Route
path='/prof'
element={login ? <Profile /> : <Navigate to='/' replace />}
/>
I realize these are the docs for v5: https://v5.reactrouter.com/web/api/Hooks/uselocation
But I suspect that before you didn't use the useLocation() hook by default?
From the url that gets parsed you can tell that the location variable not a string but an object [object%20Object]
Could you try: ${location.pathname} where you now have ${location} ?

Matched leaf route at location "/Gallary" does not have an element

I am Trying to reach the <Gallery/> Component using a Menu button with React-Router Link
so the code is for the Menu
Menu.jsx
import { Link } from 'react-router-dom';
export default function Menu({ menuOpen, setMenuOpen }) {
return (
<div className={"menu " + (menuOpen && "active")}>
<ul>
<li onClick={() => setMenuOpen(false)}>
<Link to="/">Home Page</Link>
</li>
<li onClick={() => setMenuOpen(false)}>
<Link to="/Gallery">Gallery</Link>
</li>
</ul>
</div>
);
}
and the code for APP.jsx:
import './App.scss';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { useState } from 'react';
import Gallery from './components/Gallery/Gallery';
import Menu from './components/menu/Menu';
import Topbar from './components/topbar/Topbar';
import FooterComp from './components/Footer/FooterComp';
const App = () => {
const [menuOpen, setMenuOpen] = useState(false);
return (
<>
<Router>
<Topbar menuOpen={menuOpen} setMenuOpen={setMenuOpen} />
<Menu menuOpen={menuOpen} setMenuOpen={setMenuOpen} />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/Gallery" elemtent={<Gallery />} />
</Routes>
<FooterComp />
</Router>
</>
)
}
export default App
When I click the button which is supposed to route to the <Gallery/> Component it routes to an empty component and this warning is displayed in the console
Matched leaf route at location "/Gallery" does not have an element. This means it will render an with a null value by default resulting in an "empty" page.
I searched for this problem and only router-dom version related fixes are there and you can see I'm using the correct v6 props and components.
You have a typo. element
Change
<Route path="/Gallery" elemtent={<Gallery />} />
to
<Route path="/Gallery" element={<Gallery />} />

React V6 route guarding and routing within component

Im trying to use React Routing V6 for my project.
Currently im struggeling to make the authentication and routing itself to work.
the idea of my code is:
Not authenticated user:
redirect to /login with my login component. (only login component)
Authenticated user:
Load the gameComponent component, and the rest of links inside of gamecomponent, will load inside gameComponents div class named middleContentHolder
examples:
authenticated user:
visits url /crime -> loads gamecomponent, and within gamecomponent it loads the crime component.
visits url /test -> loads gamecomponent , and within gamecomponent it loads the SideBarRight component.
not authenticated user:
vitits url /crime -> not authenticated -> redirects to /login -> loads loginmodule only.
please note that in gamecomponent component, i do have links that will load within gamecomponent.
app.js will either load the gamecomponent, or redirect user to login if not auth.
app.js:
import React from 'react';
import logo from './logo.svg';
import './App.css';
import GameComponent from './gameComponent.jsx';
import { BrowserRouter as Router } from 'react-router-dom';
import { Routes, Route, Navigate, Outlet } from 'react-router-dom';
import Crime from './components/game/crime.jsx';
import PrivateRoute from './PrivateRoute';
import Login from './components/login/login.jsx';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<GameComponent />}>
<PrivateRoute isAuth={true} path="crime" component={Crime} redirectTo='/login'/>
</Route>
</Routes>
</Router>
);
}
export default App;
Gamecomponent:
import React, {Component} from 'react';
//import Component from 'react-dom';
import SideBarRight from './components/game/sideBarRight.jsx';
import SideBarLeft from './components/game/sideBarLeft.jsx';
import Crime from './components/game/crime.jsx';
import Login from './components/login/login.jsx';
import './gameComponent.css';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import { BrowserRouter} from "react-router-dom";
class GameComponent extends Component{
constructor() {
super();
this.state = {
userData: {
user: {cash:0, bank:0, weapon:'', username: 'test', locationname: 'Bankok',
defence: 0},
rankbar: {rankpercent: 50, rank: 'Mafia'},
}
}
}
render() {
return (
<div className="main">
<div className="sidebar left">
<SideBarLeft/>
</div>
<div className="middleContentHolder">
<Route path="/" element={<Crime />} />
<Route path="/test" element={<Crime />} />
<Route path="/crime" element={<Crime />} />
<Route path="/test" element={<SideBarRight UserData={this.state.userData} />} />
<div className="col-8">
<div className="content">
<div className="header"><span>Test...</span></div>
</div>
</div>
</div>
<div className="sidebar right">
<SideBarRight UserData={this.state.userData}/>
</div>
</div>
);
}
}
export default GameComponent;
PrivateRoute:(auth is just a dummy atm)
import React from 'react';
import PropTypes from 'prop-types';
import { Route, Navigate } from 'react-router-dom';
import { useNavigate } from "react-router-dom";
import Login from './components/login/login.jsx';
import GameComponent from './gameComponent.jsx';
const PrivateRoute = ({ component: Component, redirectTo, isAuth, path, ...props }) => {
isAuth = false;
if(!isAuth) {
return <Navigate to={redirectTo} />;
}
return <Route path={path} element={<Component />} />
};
export default PrivateRoute;
update:
orginal auth was:in privateroute:
isAuth = isAuth;
one example of non-working code that would show what i want:
<Route path="/login" element={}>
<PrivateRoute isAuth={true} path="/" component={GameComponent} redirectTo='/login'>
rest of routes exist in gamecomponent..
</PrivateRoute>
If you only want GameComponent to load if use is authenticated, you will need to change your App component like this:
function App() {
return (
<Router>
<Routes>
<Route path="/login" element={<LoginComponent />} />
<PrivateRoute isAuth={true} path="/" component={GameComponent} redirectTo='/login'/>
</Routes>
</Router>
);
}
Here we are essentially putting a switch so that we can navigate to /login when there is no authentication. <Routes> is vital here, because it will only render the component that matches the exact path.
With the official release of React Router V6, the other answer is no longer valid. It will throw an error since <PrivateRoute /> isn't a <Route />.
The proper way to do it is to refactor your <PrivateRoute /> component like so...
import { Navigate, useLocation } from "react-router-dom"
const PrivateRoute = (props: { children: React.ReactNode }): JSX.Element => {
const { children } = props
// Up to you specific app to decide how to get this value
const isLoggedIn: boolean = localStorage.getItem('logged_user') !== null;
const location = useLocation()
return isLoggedIn ? (
<>{children}</>
) : (
<Navigate
replace={true}
to="/login"
state={{ from: `${location.pathname}${location.search}` }}
/>
)
}
Then whichever file you setup your routes in, you would do...
<Routes>
<Route path="/PRIVATE" element={<PrivateRoute> <PrivatePage /> </PrivateRoute>}/>
<Route path="/profile" element={<PrivateRoute> <ProfilePage /> </PrivateRoute>}/>
<Route path="/login" element={<LoginPage />}/>
<Route path="/" element={<HomePage />}/>
</Routes>
This is the proper way of doing it in V6 since only a <Route /> can be nested in a <Routes />. Then your authenticated logic gets moved into the element prop.
As an added bonus, the state={{ from: `${location.pathname}${location.search}` }} in PrivateRoute allows you to get the URL of the page they tried to enter, but was denied. This is passed to your login page, where you can redirect them back to the URL after they authenticate.
Solution for newer version
Create custom middleware <AuthGuard> and use as wrapper
<PrivateRoute> not vaild for newer version as child of <Routes>
Error "A <Route> is only ever to be used as the child of <Routes> element"
App.js
import React from 'react'
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import AuthGuard from "./Routes/AuthGuard";
function App() {
return (
<div className='App'>
<Router>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/contact' element={<Contact />} />
<Route path='/guest-page' element={<AuthGuard isGuest={true}><h1>Guest Page</h1></AuthGuard>} />
<Route path='/protected-page' element={<AuthGuard requireToken={true}><h1>ProtectedPage</h1></AuthGuard>} />
</Routes>
</Router>
</div>
);
}
export default App;
AuthGuard.js
import { Route, useNavigate } from "react-router-dom";
import { useLayoutEffect } from "react";
const ProtectedRoute = ({requireToken, guest, children, ...rest}) => {
console.log(requireToken, guest, children, rest);
const navigate = useNavigate();
const hasToken = false;
let hasAccess = (!requireToken || (requireToken && hasToken));
let navigateTo = '/?login-rquired=true';
if(guest) {
hasAccess = !hasToken;
navigateTo = '/?guest=true';
console.log("Guest > hasAccess: " + hasAccess)
}
if(requireToken){
console.log("requireToken", requireToken)
}
useLayoutEffect(()=>{
if (!hasAccess) {
console.log("Not allowed");
navigate(navigateTo);
}
},[])
return (
<>{ hasAccess ? children : "Login required" }</>
)
}
export default ProtectedRoute;

Infinite loop with React router

I am trying to figure out how to resolve an infinite loop issue.
FLOW: I have a state hook setUserData that I am passing to HomePage. From HomePage I am calling API to retrieve data and then set it in the App component.
Now when data is set and I click back button, then it into an infinite loop because userData is already set.
What can I do to fix it ?
CODE OF INTEREST:
{userData ?
<Redirect push
to={{
pathname: "/user",
search: `username=${userData.data.login}`
}}
/>
:
<Homepage setUserData={setUserData} />}
import React, { useState } from "react";
import "./App.css";
import Homepage from "./components/Homepage";
import ResultsPage from "./components/ResultsPage";
import Footer from "./components/Footer"
import { HashRouter, Switch, Route } from "react-router-dom";
import { Redirect } from "react-router-dom";
function App() {
const [userData, setUserData] = useState(null);
return (
<div className="App">
<HashRouter>
<Switch>
<Route exact path="/">
{userData ?
<Redirect push
to={{
pathname: "/user",
search: `username=${userData.data.login}`
}}
/>
:
<Homepage setUserData={setUserData} />}
</Route>
<Route path="/user">
<ResultsPage userData={userData} />
</Route>
</Switch>
</HashRouter>
{/* <Footer /> */}
</div>
);
}
export default App;
Base URL points to HomePage. When search is done on HomePage, data is passed to ResultsPage. (This is happening currently). Issue is that once userData is populated then clicking back redirects to ResultPage again. I need to be able to come back to HomePage when user clicks back button on home browser (Even if data is already loaded).
if u want achieved it, the better way is handle redirect in your homepage file
assume that
HomePage.jsx
import { useHistory} from "react-router-dom";
const HomePage = (props)=>{
const { setUserData } = props
....
const history = useHistory()
const handleClick = ()=>{
const data = HTTP.get('api')
setUserData(data)
history.push(`/user?username=${data}`)
}
...
return (
...
<button onClick={handleClick} >BUTTON</button>
...
)
}
App.jsx
function App() {
const [userData, setUserData] = useState(null);
return (
<div className="App">
<HashRouter>
<Switch>
<Route exact path="/">
<Homepage setUserData={setUserData} />}
</Route>
<Route path="/user">
<ResultsPage userData={userData} />
</Route>
</Switch>
</HashRouter>
{/* <Footer /> */}
</div>
);
}

cannot read property history because it's undefined but it is

I am getting the error cannot read property history but I defined it.
This used the work when I had it in main.jsx in my client folder but now it stops working.
The app file is in my imports folder.
import { Router, Route, Switch, Redirect } from "react-router-dom";
import createBrowserHistory from "history/createBrowserHistory";
const history = createBrowserHistory();
// App component - represents the whole app
export class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="container">
<Router history={history}>
<Switch>
<Route path="/" exact component={Home} />
<Route
path="/dashboard"
render={() =>
this.props.currentUser ? <Dashboard /> : <NoPermission />}
/>
<Route path="/test" component={Test} />
<Route component={NotFound} />
</Switch>
</Router>
</div>
);
}
}
more info:
import createBrowserHistory from "history/createBrowserHistory";
within that file createBrowserHistory is the default export.
export.default = createBrowserHistory;
When trying BrowserRouter instead of router and deleting the history const and props I get following error in my console.
modules.js:26944 Uncaught TypeError: Cannot read property 'history' of undefined
at Link.render (modules.js?hash=b38005f7c50b72cb1ea0945090b4ba307f31282f:26944)
at modules.js?hash=b38005f7c50b72cb1ea0945090b4ba307f31282f:18399
at measureLifeCyclePerf (modules.js?hash=b38005f7c50b72cb1ea0945090b4ba307f31282f:17679)
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (modules.js?hash=b38005f7c50b72cb1ea0945090b4ba307f31282f:18398)
at ReactCompositeComponentWrapper._renderValidatedComponent (modules.js?hash=b38005f7c50b72cb1ea0945090b4ba307f31282f:18425)
at ReactCompositeComponentWrapper.performInitialMount (modules.js?hash=b38005f7c50b72cb1ea0945090b4ba307f31282f:17965)
at ReactCompositeComponentWrapper.mountComponent (modules.js?hash=b38005f7c50b72cb1ea0945090b4ba307f31282f:17861)
at Object.mountComponent (modules.js?hash=b38005f7c50b72cb1ea0945090b4ba307f31282f:10622)
at ReactDOMComponent.mountChildren (modules.js?hash=b38005f7c50b72cb1ea0945090b4ba307f31282f:16977)
at ReactDOMComponent._createInitialChildren (modules.js?hash=b38005f7c50b72cb1ea0945090b4ba307f31282f:14176)
When using BrowserRouter in my main.jsx I can get it working. I can change URL's but the new views do not render. So I think there still is something wrong with the history. In this case I have not defined it but I am not receiving any errors. Any way how I can check or fix this?
import React from "react";
import { Meteor } from "meteor/meteor";
import { render } from "react-dom";
import "../imports/startup/accounts-config.js";
import App from "../imports/layouts/App.jsx";
import Test from "../imports/Test.jsx";
import { BrowserRouter } from "react-router-dom";
Meteor.startup(() => {
render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("render-target")
);
});
Going further on Kyle's answer I added withrouter to my test component.
import React, { Component } from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
class Test extends Component {
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
};
render() {
const { match, location, history } = this.props;
return (
<div>
<p>This is a test</p>
<p>
You are now at {location.pathname}
</p>
</div>
);
}
}
export default withRouter(Test);
I am using NavLinks to link to this route in my navigation bar component.
<NavLink to="/test" activeClassName="active">
Test
</NavLink>
However clicking those links does not render the test page. (the address in the URL bar does change). When I press refresh in the browser the page loads and the location.pathname shows the proper location.
If I remove the withrouter the functionality is the same.
I got it working by not using a component to nest the router in.
If somebody can explain me why I would greatly appreciate it.
import Navbar from "../components/Navbar.jsx";
import AccountsUIWrapper from "../components/AccountsUIWrapper.jsx";
//import pages
import Home from "../pages/Home.jsx";
import Dashboard from "../pages/Dashboard.jsx";
import Test from "../Test.jsx";
import NotFound from "../pages/NotFound.jsx";
import NoPermission from "../pages/NoPermission.jsx";
let currentUser = Meteor.user();
const App = () =>
<Router>
<div>
<Navbar currentUser={currentUser} />
<AccountsUIWrapper />
<p>
{currentUser ? currentUser._id : "current user id not found"}
</p>
<Switch>
<Route exact path="/" component={Home} />
<Route
path="/dashboard"
render={() => (currentUser ? <Dashboard /> : <NoPermission />)}
/>
<Route path="/test" component={Test} />
<Route component={NotFound} />
</Switch>
</div>
</Router>;
export default App;
React Router 4 has history baked into it. You can see from the documentation for BrowserRouter, HashRouter, and MemoryRouter that there is no argument for history.
If you would like to access history in React Router v4 you should use the withRouter HoC on the component that you wish to have access to it in. withRouter will make ({match, history, location }) available inside any component that it wraps.
As you can see from this line of code: var _createBrowserHistory = require('history/createBrowserHistory'); which is line 13 in BrowserRouter.js and HashRouter.js history is already included for you. It is also included in the memory router on line 9 of MemoryRouter.js.
Try changing your import at the top to import { BrowserRouter as Router, Route, Switch, Redirect } from "react-router-dom"; and then remove history={ history } from <Router />.
EDIT: Please take a look at the documentation for React Router 4. Here is a basic example.
Here is a post of the code incase the link ever goes dead.
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
const BasicExample = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</div>
</Router>
)
const Home = () => (
<div>
<h2>Home</h2>
</div>
)
const About = () => (
<div>
<h2>About</h2>
</div>
)
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${match.url}/rendering`}>
Rendering with React
</Link>
</li>
<li>
<Link to={`${match.url}/components`}>
Components
</Link>
</li>
<li>
<Link to={`${match.url}/props-v-state`}>
Props v. State
</Link>
</li>
</ul>
<Route path={`${match.url}/:topicId`} component={Topic}/>
<Route exact path={match.url} render={() => (
<h3>Please select a topic.</h3>
)}/>
</div>
)
const Topic = ({ match }) => (
<div>
<h3>{match.params.topicId}</h3>
</div>
)
export default BasicExample

Resources