React Link doesn't trigger the router changement - reactjs

In my react app I've tried lot of different router, route, and solution find on internet.
The fact is i'm using <HashRouter> from react-router-dom and redux in my app.
When I change the url in my browser the right route is triggered and the right component is loaded.
The issue :
When i click on <Link> component the url change, the history props on the router change but nothing happenned in the app...
Here are my app architecture and code :
MainApp.jsx
render(){
<Provider store={store}>
<HashRouter>
<div className="main-app">
<StickyContainer>
<Header toggleHelp={() => this.toggleOverlay()} />
<Sticky>
<Toolbar /> //Here are my <Link/>
</Sticky>
<App/>
<Footer />
</StickyContainer>
</div>
</HashRouter>
</Provider>
}
App.js
import React from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as Actions from 'Actions';
import Main from 'Components/Main/Main';
import {withRouter} from 'react-router-dom';
const App = ({elements, actions,documents,filters}) => (
<div>
<Main elements={elements} actions={actions} documents={documents} filters={filters} />
</div>
)
const mapStateToProps = state => ({
elements: state.elements,
documents: state.documents,
filters:state.filters
});
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(Actions, dispatch)
});
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(App));
And finally my Main.jsx
render(){
<div className="main-authenticated">
<Switch>
<Route path="/home" component={Home} />
<Route path="/reporting" component={Reporting} />
<Route path="/about" component={About} />
<Route path="/disconnect" component={ErrorPage} />
</Switch>
</div>
}
I already tried with a BrowserRouter, a basic Router with history but always this issue. Don't know if it's due to my project architecture or something else.
UPDATE
Moved withRouter on Main.jsx and got the same issue.
Main.jsx
render(){
<div className="main-authenticated">
<Switch>
<Route path="/home" component={Home} />
<Route path="/reporting" component={Reporting} />
<Route path="/about" component={About} />
<Route path="/disconnect" component={ErrorPage} />
</Switch>
</div>
}
export default withRouter(Main)

As #ShubhamKhatri said, I needed to export my Main component with withRouter function from react-router.
But there was an other issue, the Link included in the Toolbar component was not triggered the router due to the Sticky Component from react-sticky.
Remove the Sticky component wrapper on the MainApp correct the problem.
Final solution :
exporting Main.jsx
class Main
[...]
export default withRouter(Main);
MainApp.jsx
<Provider store={store}>
<HashRouter>
<div className="main-app">
<Header />
<Toolbar/>
<App>
<Footer />
</div>
</HashRouter>
</Provider>

Related

React router v6 detecting change in location [duplicate]

im using react router v6 and i every time i use initializing for authentication in my main file it shows this error. i cant find a solution in the internet for it. i want to render some routes only when there is a user but now it doesnt render anything.
AuthNavigator
import React, { useState, useEffect } from 'react';
import app from './firebase';
import { Router, Routes, Route } from 'react-router-dom';
import AuthStack from './stacks/AuthStack';
import AppStack from './stacks/AppStack';
import StaticStack from './stacks/StaticStack';
function AuthNavigator() {
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useState(() => app.auth().currentUser);
useEffect(() => {
const unsubscribe = app.auth().onAuthStateChanged((user) => {
if (user) {
setUser(user);
} else {
setUser(null);
}
if (initializing) {
setInitializing(false);
}
});
// cleanup subscription
return unsubscribe;
}, []);
if (initializing) return 'Loading....';
return (
<Router>
<Routes>
<Route path="*" element={<StaticStack />} />
<Route path="auth/*" element={<AuthStack user={user} />} />
<Route path="app/*" element={<AppStack user={user} />} />
</Routes>
</Router>
);
}
export default AuthNavigator;
App.js
import React from 'react';
import './App.css';
import AuthNavigator from './AuthNavigator';
import { Router } from 'react-router-dom';
function App() {
return (
<Router>
<AuthNavigator />
</Router>
);
}
export default App;
I had the same issue. My issue is because of the following reason.
I had a Header component which consists of NavLink which is a router component. I placed this Header component inside the App component. My App component was like this:
function App() {
return(
<Header/>
<Router>
<Routes>
<Route path="/" element={<Homepage/>}/>
<Route path="/shop" element={<Shop/>}/>
<Route path="/signin" element={<Signin/>}/>
</Routes>
</Router>
)
}
In the above App component, I have placed Header component outside of Router. Since in the Header component I have used NavLink which is a Router component caused this error. Then I moved Header component into the Router component then it worked fine. Finally my code looked like this:
function App() {
return(
<Router>
<Header/>
<Routes>
<Route path="/" element={<Homepage/>}/>
<Route path="/shop" element={<Shop/>}/>
<Route path="/signin" element={<Signin/>}/>
</Routes>
</Router>
)
}
Already wrapped in Router?
if your component is already wrapped in a Router. Make sure you are importing useLocation from react-router-dom instead of react-router. this worked for me.
Make sure that your App component in index.js is wrapped with BrowserRouter like this
const app = (
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
In case you run into this problem when running a test, don't forget to wrap your import of App in Router as well. My crude example is below.
import { render, screen } from '#testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import { App } from '../App';
test('renders Box', () => {
render(
<BrowserRouter>
<App />
</BrowserRouter>
);
const boxElement = screen.getByLabelText('box-outline');
expect(boxElement).toBeInTheDocument();
});
I had the same error coming up from inside a test. The component I was testing contained a NavLink component, and I wasn't rendering a Router in the test.
This error disappeared after wrapping my component in question with BrowserRouter.
I had this problem when using {useLocation} from 'react-router-dom'
function App() {
const { pathname, hash, key } = useLocation();
//function using pathname hash and key
return(
<Router>
<Header/>
<Routes>
<Route path="/" element={<Homepage/>}/>
<Route path="/shop" element={<Shop/>}/>
<Route path="/signin" element={<Signin/>}/>
</Routes>
</Router>
)
}
throws the same error even with the in the correct place
I fixed it by explicitly wrapping the useLocation() within the router
function App() {
return(
<Router>
<Header/>
<Inner/>
</Router>
)
}
function Inner() {
const { pathname, hash, key } = useLocation();
//function using pathname hash and key
return(
<Routes>
<Route path="/" element={<Homepage/>}/>
<Route path="/shop" element={<Shop/>}/>
<Route path="/signin" element={<Signin/>}/>
</Routes>
)
}
I had this error because Vite was bundling two copies of the same version of react-router-dom... check the outputted bundle for * React Router DOM and see how many copies there are.
If that's the case, the solution will differ. In my scenario I think it's because I was referencing other local npm packages using file:... once I switched to a npm workspace that fixed it.
So i fixed like this structure
index.js
import { BrowserRouter } from 'react-router-dom'
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
App.js
import { Route, Routes, Navigate } from 'react-router-dom'
<Layout>
<Routes>
<Route path="/" element={<Navigate to="/home" />} />
<Route path="/home" element={<Home />} />
{...}
<Route path="*" element={<NotFound />} />
</Routes>
</Layout>
Layout.js
<Fragment>
<Header></Header>
<main className="container">{props.children}</main>
</Fragment>
Header.js
import { Link, NavLink } from 'react-router-dom'
<header className={classes.header}>
<nav>
<NavLink
className={(navData) => (navData.isActive ? classes.active : '')}
to="/search"
>
Search
</NavLink>
</nav>
</header>
App.test.js
import { BrowserRouter } from 'react-router-dom';
it('renders learn react link', async () => {
render(<BrowserRouter><App /></BrowserRouter>)
const linkElement = await screen.findByText(/home/i)
expect(linkElement).toBeInTheDocument()
})
in my case, I faced this error when I used the HOC and context provider and placed my Routing component as the context.provider's child, and export my Provider component by HOC like this:
class Provider extends Component {
render() {
return (
<Context.Provider
value={{
something: this.state.something
}}
>
<Routing />
</Context.Provider>
);
}
}
export default HOC(Provider)
im my case, the problem was putting BrowserRouter inside App.js file, solved it by moving this into index.js file and wrapping it around App.js
Former Index.js
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<React.StrictMode>
<PersistGate persistor={persistor} >
<App/>
</PersistGate>
</React.StrictMode>
</Provider>
);
reportWebVitals();
Former App.js
import { BrowserRouter, Routes, Route, useLocation} from "react-router-dom";
const App = () => {
const {pathname} = useLocation()
return (
<div className='app'>
<BrowserRouter>
<Routes>
<Route path='/*' element={<LandingPage />} />
</Routes>
</BrowserRouter>
</div>
);
};
export default App;
New Index.js
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<React.StrictMode>
<PersistGate persistor={persistor} >
<BrowserRouter >
<App/>
</BrowserRouter>
</PersistGate>
</React.StrictMode>
</Provider>
);
reportWebVitals();
New App.js
import { Routes, Route, useLocation} from "react-router-dom";
const App = () => {
const {pathname} = useLocation()
return (
<div className='app'>
<Routes>
<Route path='/*' element={<LandingPage />} />
<Route path='/registeration' element={<Registration />} />
</Routes>
</div>
);
};
export default App;
Try this:
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';

routed component not displaying in react

I have a simple react page with a navbar and a single route.
App.js:
import React from 'react';
import { BrowserRouter as Router, Route } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import Navbar from "./components/navbar.component"
import CreateUser from "./components/create-user.component";
function App() {
return (
<Router>
<div className="container">
<Navbar />
<br />
<Route path="/user" component={CreateUser} />
</div>
</Router>
);
}
export default App;
create-user.component.js:
import React, { Component } from 'react';
export default class CreateUser extends Component {
render() {
return (
<div>
<p>You are on the Create User component!</p>
</div>
)
}
}
The navbar displays ok but if I add in the route the navbar disappears and the routed page doesn't work.
Try this:
<Route path="/user" element={<CreateUser />} />
downgrading react router dom "resolved" the issue, not ideal but at least it works now :)
npm uninstall react-router-dom && npm install react-router-dom#5.2.0
if you are using react-router-v6 then component don't support anymore this is the correct syntax
<BrowserRouter>
<div className="container">
<Navbar />
<br />
<Routes>
<Route path="/user" element={<CreateUser />} />
<Route path="*" element={<Navigate to="/user" />} />
</Routes>
</div>
</BrowserRouter>
i also recommend go through documentation first because there is quite changes in react router
React Router Documentation

useParams() for none route components in ReactJS

Please go through my App.js
<Provider store={reduxStore}>
<Router basename={process.env.PUBLIC_URL}>
<Container>
<Navigation /> <----------------------------------- HERE
<div className="wrapper">
<Switch>
<PublicRoute exact path={PATHS.HOME} component={Home} />
</Switch>
</div>
<Footer/> <---------------------------------------- HERE
</Container>
</Router>
</Provider>
Navigation is use in all components so I added that component in App.js. For sure, this component has no route linked to it. BUT:
In Navigation and Footer, I want to use useParams().
Navigation.js
import React from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import { withRouter, useParams } from 'react-router-dom';
const Navigation = (props) => {
const { slug } = props;
return (
<>
<Navbar>
<Nav>
<Nav.Link>
Home
</Nav.Link>
</Nav>
</Navbar>
</>
);
};
export default withRouter(Navigation);
My current url:
localhost:3000/event/:slug
I do recieve props, but I could not find slug
try remove exact ?
<Switch>
<PublicRoute path={PATHS.HOME} component={Home} />
</Switch>

history object with central Routes file

I have seen many examples over the net on how to use history object with React Router. However, I havent stumbled upon a case where the example shows a central routes file as shown below:
routes.js
const RouteList = () => (
<main>
<Switch>
<Route path="/" exact component={HomePage} />
<Route component={Error} />
</Switch>
</main>
);
export default RouteList;
App.js
render() {
return (
<div>
<Header />
<RouteList />
<Footer />
</div>
);
}
history.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
Can someone shed some light how i can use history with my centralized routes? Or if there is another similar thread please let me know thank you so much.
The custom history object need to be provided as a prop to the Router Provider component. In your case you can Specify a Router Provider in App.js or RouteList depending on whether Header and Footer also need Router props or not.
import browserHistory from './history.js';
...
render() {
return (
<Router history={browserHistory}>
<div>
<Header />
<RouteList />
<Footer />
</div>
</Router>
);
}
Also in your history.js file, import createBrowserHistory like
import createBrowserHistory from 'history/createBrowserHistory';

alternative for IndexRoute in react-router 4

I am following a course where the author has written some code for routing in react using react-router version 3.
<Router history={browserHistory} >
<Route path="/" component={Main}>
<IndexRoute component={PhotoGrid}></IndexRoute>
<Route path="/view/:postId" component={Single}>
</Route>
</Router>
While following the course(in which I am using using router version 4), I came to know that now the <IndexRoute> doesn't exists. I googled for alternative and found that exact is the alternative. But I don't know how to use exact for meeting this requirement. The author want the Main component always on the DOM, and based on the url only the child components should change(i.e Single or PhotoGrid).
I tried below code, which is of course the wrong one:
<BrowserRouter>
<Switch>
<Route path="/" component={Main} />
<Route exact path="/" component={PhotoGrid} />
<Route path="/veiw/:postId" component={Single} />
</Switch>
</BrowserRouter>
My components are:
class Main extends React.Component {
render() {
return (
<div>
<h1>
<Link to="/">Reduxtagram</Link>
</h1>
{React.cloneElement(this.props.children, this.props)}
</div>
);
}
}
class PhotoGrid extends React.Component {
render() {
return <div className="photo-grid">PhotoGrid</div>;
}
}
class Single extends React.Component {
render() {
return <div className="single-photo">Single</div>;
}
}
The Switch component will only render the first Route that is a match.
You could use the Main component as a regular component and use the Switch as children for that.
<BrowserRouter>
<Main>
<Switch>
<Route exact path="/" component={PhotoGrid} />
<Route path="/view/:postId" component={Single} />
</Switch>
</Main>
</BrowserRouter>
i know this is an old question but im following the same course and i decided to update all the libraries, basically i had to restructure the app like this:
reduxtagram.js :
import React from 'react';
import {render} from 'react-dom';
import App from './components/App';
import {Provider} from 'react-redux';
import store from './store';
// import css
import css from './styles/style.styl';
// import react router deps
const router = (
<Provider store={store}>
<App/>
</Provider>
)
render(router , document.getElementById('root'));
main.js:
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import PhotoGrid from './PhotoGrid';
import Single from './Single';
import {Router, Route} from 'react-router-dom';
import {history} from '../store';
export default class Main extends Component {
render() {
return (
<div>
<Router history={history}>
<Route
path="/"
render={(routeProps) => (
<h1>
<Link to='/'>Reduxstagram </Link>
</h1>
)}/>
<Route
path="/grid"
render={(routeProps) => (
<PhotoGrid {...routeProps} {...this.props} />
)}/>
<Route
path="/grid/view/:postID"
render={(routeProps) => (
<Single {...routeProps} {...this.props} />
)}/>
</Router>
</div>
)
}
}

Resources