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

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

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} ?

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

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

I use React Router 4 in React page but I can't see my component in Route path

[
import React, { Component } from "react";
import { BrowserRouter as Router , Route } from "react-router-dom";
const NewRout = () => {
return(
<p> MY ROUTE </p>
);
}
class App extends Component {
render() {
return (
<Router>
<Route path="/signin" Component={NewRout} />
</Router>
);
}
}
export default App;
]1I'm using router in my react page. But I can't see output.
I import BrowserRouter and Route from react-route-dom
and try to show mt component inside the route.but this is not working for me.Please help me how to solve this issue. Thanks
<BrowserRouter><Route path="signin" Component={Signin} /></BrowserRouter>
You have a mistake in your path:
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Link } from "react-router-dom";
import "./styles.css";
const Home = () => (
<h1>
Home <Link to="/signin">SING</Link>
</h1>
);
const SingIn = () => (
<h1>
<Link to="/">Home</Link>
This is singin page
</h1>
);
ReactDOM.render(
<div>
<BrowserRouter>
<div>
<Route exact path="/" component={Home} />
<Route path="/signin" component={SingIn} />
</div>
</BrowserRouter>
</div>,
document.getElementById("root")
);
Now locate to http://localhost:port/singin you will see your component.
Note: I added a / before your path. This denotes that you are going to signin from your root that is /.
You need to use a prop called exact which matches for exact Route.
Try this SandBox
https://codesandbox.io/s/moj8kxp0nx

React Router not rendering correctly

I had this working before, but I've made some changes in my app structure which has broken the routes. If I manually go to the route in the browser it works (the correct content loads). The href attribute is not rendering in the html.
I have a warning message in my console:
Warning: Failed prop type: Invalid prop to supplied to Link.
I don't nderstand why it's invalid.
What can I do to improve the code and fix the problem?
Index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import Header from './Header';
import Routes from './Routes';
import '../less/imports.less';
const App = () => (
<div>
<Header />
<main>
<Routes />
</main>
</div>
);
if (typeof window !== 'undefined') {
ReactDOM.render(
(
<App />
), document.getElementById('app'),
);
}
Routes.jsx:
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Dashboard from './Dashboard';
import About from './About';
import Blog from './Blog';
class Routes extends React.Component {
constructor(props) {
super(props);
this.state = {
active: 'active',
};
}
render() {
return (
<Router>
<div>
<nav>
<ul className="block-group">
<li className={this.state.active}><Link to={Dashboard}>Dashboard</Link></li>
<li><Link to={About}>About</Link></li>
<li><Link to={Blog}>Blog</Link></li>
</ul>
</nav>
<Route exact path="/" component={Dashboard} />
<Route path="/about" component={About} />
<Route path="/blog" component={Blog} />
</div>
</Router>
);
}
}
export default Routes;
I have just fixed this by changing the to href to the actual route strings. Not sure how it worked before.
<li className={this.state.active}><Link to="/">Dashboard</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/blog">Blog</Link></li>

named path not working in react-router-dom react js

I am using react-router-dom for routing purposes. This is my code:
//Libraries
import React from 'react';
import { Tabs, Tab } from 'react-tabify';
import { connect } from 'react-redux';
import {
BrowserRouter as Router,
Route,
withRouter
} from 'react-router-dom';
//Components
import HomePage from './containers/HomePage';
import PersonsPage from './containers/PersonsPage';
const App = () => (
<Router>
<div style={{height: '100%', width: '100%'}}>
<Route exact path="/" component={HomePage}/>
<Route exact path="/:id" component={PersonsPage}/>
// here i want to use like this --> <Route exact path="/personPage/:id" component={PersonsPage}/>
</div>
</Router>
)
export default withRouter(connect()(App));
Route exact path="/:id" component={PersonsPage}/> this works , but this Route exact path="/personPage/:id" component={PersonsPage}/> this didn't works for me . Can someone clarify/help me from this pls
I think you should simply wrap your routers in Switch component. But remember to put <Route exact path="/:id" component={PersonsPage}/> as last entry.
Here you have an example in one file:
import React, { Component } from 'react';
import { render } from 'react-dom';
import {
BrowserRouter as Router,
Route,
Switch,
Link,
withRouter
} from 'react-router-dom';
const HomePage = () => (
<div>
<h1>HomePage</h1>
<ul>
<li><Link to="/user/Tom">Tom</Link></li>
<li><Link to="/user/John">John</Link></li>
<li><Link to="/user/Andy">Andy</Link></li>
</ul>
</div>
);
const PersonsPage = (props) => (
<div>
<h1>Profile: {props.match.params.name}</h1>
</div>
);
class App extends Component {
render() {
return (
<Switch>
<Route exact path="/" component={HomePage}/>
<Route exact path="/user/:name" component={PersonsPage}/>
</Switch>
);
}
}
const AppWithRouter = withRouter(App);
render(
<Router>
<AppWithRouter />
</Router>
, document.getElementById('root')
);
Here you have link to working version https://stackblitz.com/edit/react-qqpraz

Resources